«Решайте примеры, используя индексы и свойства»
Download 52.88 Kb.
|
2. Индексаторы
Индексаторы позволяют индексировать объекты и обращаться к данным по индексу. Фактически с помощью индексаторов мы можем работать с объектами как с массивами. По форме они напоминают свойства со стандартными блоками get и set, которые возвращают и присваивают значение. Формальное определение индексатора:
В отличие от свойств индексатор не имеет названия. Вместо него указывается ключевое слово this, после которого в квадратных скобках идут параметры. Индексатор должен иметь как минимум один параметр. Посмотрим на примере. Допустим, у нас есть класс Person, который представляет человека, и класс Company, который представляет некоторую компанию, где работают люди. Используем индексаторы для определения класса Company:
Для хранения персонала компании в классе определен массив personal, который состоит из объектов Person. Для доступа к этим объектам определен индексатор:
Индексатор в принципе подобен стандартному свойству. Во-первых, для индексатора определяется тип в данном случае тип Person. Тип индексатора определяет, какие объекты будет получать и возвращать индексатор. Во-вторых, для индексатора определен параметр int index, через который обращаемся к элементам внутри объекта Company. Для возвращения объекта в индексаторе определен блок get:
Поскольку индексатор имеет тип Person, то в блоке get нам надо возвратить объект этого типа с помощью оператора return. Здесь мы можем определить разнообразную логику. В данном случае просто возвращаем объект из массива personal. В блоке set, как и в обычном свойстве, получаем через параметр value переданный объект Person и сохраняем его в массив по индексу.
После этого мы можем работать с объектом Company как с набором объектов Person:
Стоит отметить, что если индексатору будет передан некорректный индекс, который отсутствует в массиве person, то мы получим исключение, как и в случае обращения напрямую к элементам массива. В этом случае можно предусмотреть какую-то дополнительную логику. Например, проверять переданный индекс:
Здесь в блоке get если переданный индекс имеется в массиве, то возвращаем объект по индексу. Если индекса нет в массиве, то генерируем исключение. Аналогично в блоке set устанавливаем значение по индексу, если индекс есть в массиве. Индексы Индексатор получает набор индексов в виде параметров. Однако индексы необязательно должны представлять тип int, устанавливаемые/возвращаемые значения необязательно хранить в массиве. Например, мы можем рассматривать объект как хранилище атрибутов/свойств и передавать имя атрибута в виде строки:
В данном случае индексатор в классе User в качестве индекса получает строку, которая хранит название атрибута (в данном случае название поля класса). В блоке get в зависимости от значения строкового индекса возвращается значение того или иного поля класса. Если передано неизвестное название, то генерируется исключение. В блоке set похожая логика - по индексу узнаем, для какого поля надо установить значение. Применение нескольких параметров Также индексатор может принимать несколько параметров. Допустим, у нас есть класс, в котором хранилище определено в виде двухмерного массива или матрицы:
Теперь для определения индексатора используются два индекса - i и j. И в программе мы уже должны обращаться к объекту, используя два индекса:
Следует учитывать, что индексатор не может быть статическим и применяется только к экземпляру класса. Но при этом индексаторы могут быть виртуальными и абстрактными и могут переопределяться в произодных классах. Блоки get и set Как и в свойствах, в индексаторах можно опускать блок get или set, если в них нет необходимости. Например, удалим блок set и сделаем индексатор доступным только для чтения:
Также мы можем ограничивать доступ к блокам get и set, используя модификаторы доступа. Например, сделаем блок set приватным:
Перегрузка индексаторов Подобно методам индексаторы можно перегружать. В этом случае также индексаторы должны отличаться по количеству, типу или порядку используемых параметров. Например:
В данном случае класс Company содержит две версии индексатора. Первая версия получает и устанавливает объект Person по индексу, а вторая - только получае объект Person по его имени. Переменные-ссылки и возвращение ссылки Кроме параметров метода, которые с помощью модификатора ref позволяют передавать значение по ссылке, C# также позволяет с помощью ключевого слова ref возвращать ссылку из метода и определять переменную, которая будет хранить ссылку. Переменная-ссылка Для определения локальной переменной-ссылки (ref local) перед ее типом ставится ключевое слово ref: int x = 5; ref int xRef = ref x; Здесь переменная xRef указывает не просто на значение переменной x, а на область в памяти, где располагается эта переменная. Для этого перед x также указывается ref. При этом мы не можем просто определить переменную-ссылку, нам обязательно надо присвоить ей некоторое значение. Так, следующий код вызовет ошибку: ref int xRef; // ошибка Получив ссылку, мы можем манипулировать значением по этой ссылке. Например: int x = 5; ref int xRef = ref x; Console.WriteLine(x); // 5 xRef = 125; Console.WriteLine(x); // 125 x = 625; Console.WriteLine(xRef); // 625 Ссылка как результат функции Для возвращения из функции ссылки в сигнатуре функции перед возвращаемым типом, а также после оператора return следует указать ключевое слово ref: int[] numbers = { 1, 2, 3, 4, 5, 6, 7 }; ref int numberRef = ref Find(4, numbers); // ищем число 4 в массиве numberRef = 9; // заменяем 4 на 9 Console.WriteLine(numbers[3]); // 9 ref int Find(int number, int[] numbers) { for (int i = 0; i < numbers.Length; i++) { if (numbers[i] == number) { return ref numbers[i]; // возвращаем ссылку на адрес, а не само значение } } throw new IndexOutOfRangeException("число не найдено"); } В методе Find ищем число в массиве, но вместо самого значения числа возвращаем ссылку на него в памяти. Для этого в сигнатуре метода в качестве типа результата функции указывается не просто int, а ref int. Кроме того, в самом методе после слова return также ставится ref: return ref numbers[i]; Тем самым мы получаем не просто значение, а ссылку на объект в памяти. В методе Main для определения переменной, которая будет содержать ссылку, используется ключевое слово ref. При вызове метода также указывается слово ref: ref int numberRef = ref Find(7, numbers); В итоге переменная numberRef будет содержать ссылку на объект int, и через данную переменную в последствиии мы можем изменить объект по этой ссылке. Другой пример - возвращение ссылки на максимальное число из двух: int a = 5; int b = 8; ref int pointer = ref Max(ref a, ref b); pointer = 34; // меняем значением максимального числа Console.WriteLine($"a: {a} b: {b}"); // a: 5 b: 34 ref int Max(ref int n1, ref int n2) { if (n1 > n2) return ref n1; else
} Стоит обратить внимание, что параметры метода в этом случае определены с ключевым словом ref. При определении метода, который возвращает ссылку, следует учитывать, что такой метод естественно не может иметь тип void. Кроме того, такой метод не может возвращать: Значение null Константу Значение перечисления enum Свойство класса или структуры Поле для чтения (которое имеет модификатор read-only) Download 52.88 Kb. Do'stlaringiz bilan baham: |
ma'muriyatiga murojaat qiling