Зачем нужны индексаторы c
Индексаторы позволяют индексировать объекты и обращаться к данным по индексу. Фактически с помощью индексаторов мы можем работать с объектами как с массивами. По форме они напоминают свойства со стандартными блоками get и set , которые возвращают и присваивают значение.
Формальное определение индексатора:
возвращаемый_тип this [Тип параметр1, . ] < get < . >set < . >>
В отличие от свойств индексатор не имеет названия. Вместо него указывается ключевое слово this , после которого в квадратных скобках идут параметры. Индексатор должен иметь как минимум один параметр.
Посмотрим на примере. Допустим, у нас есть класс Person , который представляет человека, и класс Company , который представляет некоторую компанию, где работают люди. Используем индексаторы для определения класса Company:
class Person < public string Name < get;>public Person(string name) => Name=name; > class Company < Person[] personal; public Company(Person[] people) =>personal = people; // индексатор public Person this[int index] < get =>personal[index]; set => personal[index] = value; > >
Для хранения персонала компании в классе определен массив personal , который состоит из объектов Person. Для доступа к этим объектам определен индексатор:
public Person this[int index]
Индексатор в принципе подобен стандартному свойству. Во-первых, для индексатора определяется тип в данном случае тип Person. Тип индексатора определяет, какие объекты будет получать и возвращать индексатор.
Во-вторых, для индексатора определен параметр int index , через который обращаемся к элементам внутри объекта Company.
Для возвращения объекта в индексаторе определен блок get :
get => personal[index];
Поскольку индексатор имеет тип Person, то в блоке get нам надо возвратить объект этого типа с помощью оператора return. Здесь мы можем определить разнообразную логику. В данном случае просто возвращаем объект из массива personal.
В блоке set , как и в обычном свойстве, получаем через параметр value переданный объект Person и сохраняем его в массив по индексу.
set => personal[index] = value;
После этого мы можем работать с объектом Company как с набором объектов Person:
var microsoft = new Company(new[] < new Person("Tom"), new Person("Bob"), new Person("Sam"), new Person("Alice") >); // получаем объект из индексатора Person firstPerson = microsoft[0]; Console.WriteLine(firstPerson.Name); // Tom // переустанавливаем объект microsoft[0] = new Person("Mike"); Console.WriteLine(microsoft[0].Name); // Mike
Стоит отметить, что если индексатору будет передан некорректный индекс, который отсутствует в массиве person, то мы получим исключение, как и в случае обращения напрямую к элементам массива. В этом случае можно предусмотреть какую-то дополнительную логику. Например, проверять переданный индекс:
class Company < Person[] personal; public Company(Person[] people) =>personal = people; // индексатор public Person this[int index] < get < // если индекс имеется в массиве if (index >= 0 && index < personal.Length) return personal[index]; // то возвращаем объект Person по индексу else throw new ArgumentOutOfRangeException(); // иначе генерируем исключение >set < // если индекс есть в массиве if (index >= 0 && index < personal.Length) personal[index] = value; // переустанавливаем значение по индексу >> >
Здесь в блоке get если переданный индекс имеется в массиве, то возвращаем объект по индексу. Если индекса нет в массиве, то генерируем исключение. Аналогично в блоке set устанавливаем значение по индексу, если индекс есть в массиве.
Индексы
Индексатор получает набор индексов в виде параметров. Однако индексы необязательно должны представлять тип int, устанавливаемые/возвращаемые значения необязательно хранить в массиве. Например, мы можем рассматривать объект как хранилище атрибутов/свойств и передавать имя атрибута в виде строки:
User tom = new User(); // устанавливаем значения tom["name"] = "Tom"; tom["email"] = "tom@gmail.ru"; tom["phone"] = "+1234556767"; // получаем значение Console.WriteLine(tom["name"]); // Tom class User < string name = ""; string email = ""; string phone = ""; public string this[string propname] < get < switch (propname) < case "name": return name; case "email": return email; case "phone": return phone; default: throw new Exception("Unknown Property Name"); >> set < switch (propname) < case "name": name = value; break; case "email": email = value; break; case "phone": phone = value; break; >> > >
В данном случае индексатор в классе User в качестве индекса получает строку, которая хранит название атрибута (в данном случае название поля класса).
В блоке get в зависимости от значения строкового индекса возвращается значение того или иного поля класса. Если передано неизвестное название, то генерируется исключение. В блоке set похожая логика — по индексу узнаем, для какого поля надо установить значение.
Применение нескольких параметров
Также индексатор может принимать несколько параметров. Допустим, у нас есть класс, в котором хранилище определено в виде двухмерного массива или матрицы:
class Matrix < int[,] numbers = new int[,] < < 1, 2, 4 >, < 2, 3, 6 >, < 3, 4, 8 >>; public int this[int i, int j] < get =>numbers[i, j]; set => numbers[i, j] = value; > >
Теперь для определения индексатора используются два индекса — i и j. И в программе мы уже должны обращаться к объекту, используя два индекса:
Matrix matrix = new Matrix(); Console.WriteLine(matrix[0, 0]); matrix[0, 0] = 111; Console.WriteLine(matrix[0, 0]);
Следует учитывать, что индексатор не может быть статическим и применяется только к экземпляру класса. Но при этом индексаторы могут быть виртуальными и абстрактными и могут переопределяться в произодных классах.
Блоки get и set
Как и в свойствах, в индексаторах можно опускать блок get или set, если в них нет необходимости. Например, удалим блок set и сделаем индексатор доступным только для чтения:
class Matrix < int[,] numbers = new int[,] < < 1, 2, 4 >, < 2, 3, 6 >, < 3, 4, 8 >>; public int this[int i, int j] < get =>numbers[i, j]; > >
Также мы можем ограничивать доступ к блокам get и set, используя модификаторы доступа. Например, сделаем блок set приватным:
class Matrix < int[,] numbers = new int[,] < < 1, 2, 4 >, < 2, 3, 6 >, < 3, 4, 8 >>; public int this[int i, int j] < get =>numbers[i, j]; private set => numbers[i, j] = value; > >
Перегрузка индексаторов
Подобно методам индексаторы можно перегружать. В этом случае также индексаторы должны отличаться по количеству, типу или порядку используемых параметров. Например:
var microsoft = new Company(new Person[] < new("Tom"), new("Bob"), new("Sam") >); Console.WriteLine(microsoft[0].Name); // Tom Console.WriteLine(microsoft["Bob"].Name); // Bob class Person < public string Name < get;>public Person(string name) => Name=name; > class Company < Person[] personal; public Company(Person[] people) =>personal = people; // индексатор public Person this[int index] < get =>personal[index]; set => personal[index] = value; > public Person this[string name] < get < foreach (var person in personal) < if (person.Name == name) return person; >throw new Exception("Unknown name"); > > >
В данном случае класс Company содержит две версии индексатора. Первая версия получает и устанавливает объект Person по индексу, а вторая — только получае объект Person по его имени.
Есть ли смысл применять индексаторы?
Появилась необходимость разобраться с индексаторами. Тема не сложная но лучше проработать на будущее. Зачем они нужны? Я так понял для того чтобы при создании экземпляра класса мы могли сразу работать с несколькими экземплярами нашего класса, так что-ли? Почему бы нам просто не сделать коллекцию например для работы с несколькими экземплярами класса? Интересуют насколько полезно употреблять индексаторы? Они несут практическую пользу типо уменьшение затрат ресурсов на работу программы, или например ускорение работы программы?
Отслеживать
задан 18 янв 2019 в 17:02
Gnom Skull Gnom Skull
513 4 4 серебряных знака 18 18 бронзовых знаков
Цитата «Индексаторы позволяют индексировать объекты и обращаться к данным по индексу. Фактически с помощью индексаторов мы можем работать с объектами как с массивами.» Для этого и нужны.
18 янв 2019 в 17:22
Индексатор это то же свойство, только параметризированное, т.е семантически связанная пара методов (ну или один метод), но дополнительно несущее семантику Item. Вот вы и должны думать, должен ваш класс нести такой смысл или нет, это решение полностью на вашей совести. Любой код, использующий индексатор можно переписать без него, с обычными методами. Никаких ускорений или замедлений индексаторы не дают.
18 янв 2019 в 17:39
Что интересно, в CIL есть поддержка и других параметризированных свойств, помимо индексатора, чего нет в C#, но есть, например, в VB
18 янв 2019 в 17:42
Ну и как и любое свойство, индексатор может возвращать любой тип, например, если вы напишете класс шахматная доска, то он может возвращать с помощью индексатора конкретную ячейку: Cell this[int x, int y] < . >
Зачем нужны индексаторы c

Индексаторы в C#
В этой статье я расскажу о том, что такое индексаторы, зачем они нужны, как и когда ими пользоваться в своих программах, написанных на языке C#. И так, индексаторы нужны в тех случаях, когда мы пишем класс, ведущий себя как некая коллекция, например, в нем есть поле-массив, например, такой класс:
//Класс-коллекция целых чисел class SomeCollection < //Конструктор, создает объект коллекцию, с указанием её размера public SomeCollection(int aSize) < collection = new int[aSize]; >//Свойство, возвращающее размер коллекции (только для чтения) public int Size < get < return collection.Length; >> //Поле-массив, для хранения элементов коллекции private int[] collection; >
Как видно из примера, в классе есть поле-массив, есть конструктор, который задает размер массива, и свойство, возвращающее размер массива «внешнему миру». Но нужно же еще как-то организовать доступ к элементам массива, чтобы пользователи нашего класса могли изменять и получать содержимое внутреннего массива. Тут конечно можно написать пару методов, один будет возвращать значение элемента с указанным, в качестве аргумента метода, индексом, а второй метод, будет устанавливать значение указанного (тем же способом) элемента. И выглядело бы это примерно так:
//Клсаа-коллекция целых чисел class SomeCollection < //Конструктор, создает объект коллекцию, с указанием её размера public SomeCollection(int aSize) < collection = new int[aSize]; >//Метод, возвращающий значение элемента, по указанному индексу public int GetElem(int anIndex) < return collection[anIndex]; >//Метод, устанавливающий значение элемента, по указанному индексу public void SetElem(int aValue, int anIndex) < collection[anIndex] = aValue; >//Свойство, возвращающее размер коллекции (только для чтения) public int Size < get < return collection.Length; >> //Поле-массив, для хранения элементов коллекции private int[] collection; >
А использование объектов данного класса могло бы выглядеть так:
//Создать коллекцию SomeCollection tmpColl = new SomeCollection(20); //Перебрать все элементы коллекции for (int i = 0; i < tmpColl.Size; i++) < //Установить значение элементу с индексом i tmpColl.SetElem(i + 1, i); >//Перебрать все элементы коллекции for (int i = 0; i < tmpColl.Size; i++) < //Вывести в консоль элемент коллекции с индексом i Console.Write(tmpColl.GetElem(i) + " "); >//Перевод строки Console.WriteLine("");
Можете создать новый проект, и проверить это дело на практике… Но можно решить задачу организации доступа к элементам встроенного в объект массива гораздо элегантнее! Вот тут мы и будем использовать индексаторы. Давайте добавим в наш класс индексатор, а выглядеть он будет так:
/* * Индексатор (похож на свойство, но со специфическим именем и * дополнительным параметром, указанным в квадратных скобках) */ public int this[int anIndex] < //Метод доступа get get < return collection[anIndex]; >//Метод доступа set set < collection[anIndex] = value; >>
Как видите, индексатор очень похож на свойство, но у него должно быть специфическое имя «this» и дополнительный параметр в квадратных скобках (которого нет у обычного свойства).
Вообще this — это ключевое слово языка C# и оно имеет особое значение (используется не только в индексаторах), о котором я как-нибудь расскажу в одной из статей.
Этот дополнительный параметр используется как индекс, для обращения к внутреннему массиву. Использование объектов класса, в который мы добавили индексатор выглядит как использование массивов:
//Создать коллекцию SomeCollection tmpColl = new SomeCollection(20); //Перебрать все элементы коллекции for (int i = 0; i < tmpColl.Size; i++) < //Установить значение элементу с индексом i tmpColl[i] = i + 1; >//Перебрать все элементы коллекции for (int i = 0; i < tmpColl.Size; i++) < //Вывести в консоль элемент коллекции с индексом i Console.Write(tmpColl[i] + " "); >//Перевод строки Console.WriteLine("");
Согласитесь, этот вариант короче и удобнее! Ну пока хватит информации об индексаторах, как-нибудь я подробнее рассажу о них…
Добавить комментарий Отменить ответ
Для отправки комментария вам необходимо авторизоваться.
Индексаторы

Программисты хорошо знакомы с процессом доступа к индивидуальным элементам, содержащимся в стандартных массивах, через операцию индекса ([]). В C# имеется возможность проектировать специальные классы и структуры, которые могут быть индексированы подобно стандартному массиву, посредством определения . Это конкретное языковое средство наиболее полезно при создании специальных типов коллекций (обобщенных и необобщенных). Индексаторы могут быть одно- или многомерными.
Одномерные индексаторы
Ниже приведена общая форма одномерного индексатора:
тип_элемента this[int индекс] < // Аксессор для получения данных, get < // Возврат значения, которое определяет индекс. >// Аксессор для установки данных, set < // Установка значения, которое определяет индекс. >>
где тип_элемента обозначает конкретный тип элемента индексатора. Следовательно, у каждого элемента, доступного с помощью индексатора, должен быть определенный тип_элемента. Этот тип соответствует типу элемента массива. Параметр индекс получает конкретный индекс элемента, к которому осуществляется доступ. Формально этот параметр совсем не обязательно должен иметь тип int, но поскольку индексаторы, как правило, применяются для индексирования массивов, то чаще всего используется целочисленный тип данного параметра.
В теле индексатора определены два аксессора (т.е. средства доступа к данным): get и set. Аксессор подобен методу, за исключением того, что в нем не объявляется тип возвращаемого значения или параметры. Аксессоры вызываются автоматически при использовании индексатора, и оба получают индекс в качестве параметра. Так, если индексатор указывается в левой части оператора присваивания, то вызывается аксессор set и устанавливается элемент, на который указывает параметр индекс. В противном случае вызывается аксессор get и возвращается значение, соответствующее параметру индекс. Кроме того, аксессор set получает неявный параметр value, содержащий значение, присваиваемое по указанному индексу.
Давайте рассмотрим пример:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 < class MyArr < int[] arr; public int Length; public MyArr(int Size) < arr = new int[Size]; Length = Size; >// Создаем простейший индексатор public int this[int index] < set < arr[index] = value; >get < return arr[index]; >> > class Program < static void Main() < MyArr arr1 = new MyArr(Size: 5); Random ran = new Random(); // Инициализируем каждый индекс экземпляра класса arr1 for (int i = 0; i < arr1.Length; i++) < arr1[i] = ran.Next(1,100); Console.Write("\t", arr1[i]); > Console.ReadLine(); > > >
В текущем классе MyArr определен индексатор, позволяющий вызывающему коду идентифицировать подэлементы с применением числовых значений. Однако надо понимать, что это не обязательное требование метода-индексатора.
Следует особо подчеркнуть, что индексатор совсем не обязательно должен оперировать массивом. Его основное назначение — предоставить пользователю функциональные возможности, аналогичные массиву.
На применение индексаторов накладываются два существенных ограничения. Во-первых, значение, выдаваемое индексатором, нельзя передавать методу в качестве параметра ref или out, поскольку в индексаторе не определено место в памяти для его хранения. И во-вторых, индексатор должен быть членом своего класса и поэтому не может быть объявлен как static.
Многомерные индексаторы
Можно также создавать индексатор, принимающий несколько параметров:
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 < class MyArr < int[,] arr; // Размерность двухмерного массива public int rows, cols; public int Length; public MyArr(int rows, int cols) < this.rows = rows; this.cols = cols; arr = new int[this.rows, this.cols]; Length = rows * cols; >// Индексатор public int this[int index1, int index2] < get < return arr[index1, index2]; >set < arr[index1, index2] = value; >> > class Program < static void Main(string[] args) < Random ran = new Random(); Console.WriteLine("Arr1: \n"); MyArr arr1 = new MyArr(4,5); for (int i = 0; i < arr1.rows - 1; i++) < for (int j = 0; j < arr1.cols - 1; j++) < arr1[i, j] = ran.Next(1,20); Console.Write(arr1[i, j] + "\t"); >Console.WriteLine(); > Console.ReadLine(); > > >