На этом шаге мы рассмотрим пример такой перегрузки.
Индексаторы можно перегружать. Перегрузка индексаторов означает, что в классе может быть описано несколько индексаторов, которые должны отличаться количеством и/или типом индексов. Пример класса с перегрузкой индексатора можно найти в программе ниже.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace pr135_1 { // Класс с несколькими индексаторами: class MyClass{ // Закрытое целочисленное поле: private int[] nums; // Конструктор класса с одним аргументом: public MyClass(int size) { // Создание массива: nums = new int[size]; // Заполнение массива: for (int k = 0; k < nums.Length; k++) { // Использование индексатора: this[k] = k + 1; } } // Переопределение метода ToString(): public override string ToString(){ // Текстовая переменная: string txt = "Содержимое объекта:\n"; // Формирование текстовой строки: for (int k = 0; k < nums.Length; k++){ // Используется индексатор с целочисленным // индексом: txt += this[k] + (k == nums.Length - 1? "\n": " "); } // Результат метода: return txt; } // Индексатор с целочисленным индексом: public int this[int k]{ // Аксессор для считывания значения: get { return nums[k % nums.Length]; } // Аксессор для присваивания значения: set { nums[k % nums.Length] = value; } } // Индексатор с символьным индексом: public int this[char s]{ // Аксессор для считывания значения: get { // Используется индексатор с целочисленным // индексом: return this[s - 'a']; } // Аксессор для присваивания значения: set { // Используется индексатор с целочисленным // индексом: this[s - 'a'] = value; } } // Индексатор с целочисленным и текстовым индексом: public int this[int k, string t] { // Аксессор для считывания значения: get { // Используется индексатор с символьным индексом: return this[t[k]]; } // Аксессор для присваивания значения: set { // Используется индексатор с символьным индексом: this[t[k]] = value; } } // Индексатор с текстовым и целочисленным индексом: public int this[string t, int k] { // Аксессор для считывания значения: get { // Использование индексатора с целочисленным и // текстовым индексом: return this[k, t]; } // Аксессор для присваивания значения: set { // Использование индексатора с целочисленным и // текстовым индексом: this[k, t] = value; } } } // Класс с главным методом: class Program { // Главный метод: static void Main() { // Целочисленная переменная: int n = 6; // Создание объекта: MyClass obj = new MyClass(n); // Проверка содержимого объекта: Console.WriteLine(obj); // Поэлементное отображение содержимого массива: for (int k = 0; k < n + 3; k++) { // Объект с целочисленным индексом: Console.Write(obj[k] + " "); } Console.WriteLine("\n"); // Объект с целочисленным индексом: obj[1] = 7; obj[n + 3] = 8; Console.WriteLine(obj); // Объект с символьным индексом: obj['a'] = 9; obj['k'] = 0; // Проверка содержимого объекта: Console.WriteLine(obj); Console.WriteLine("Проверка:"); // Поэлементное отображение содержимого массива: for(char s = 'a'; s < 'a' + n + 3; s++) { // Объект с символьным индексом: Console.Write(obj[s] + " "); } Console.WriteLine("\n"); // Объект с целочисленным и текстовым индексом: obj[4, "alpha"] = 0; obj["bravo", 0] = 6; // Проверка содержимого массива: Console.WriteLine(obj); // Текстовая переменная: string txt = "abc"; Console.WriteLine("Проверка:"); // Отображение значений элементов массива: for (int k = 0; k < txt.Length; k++) { // Объект с двумя индексами: Console.WriteLine(obj[k, txt] + ": " + obj[txt, k]); } // Задержка: Console.ReadLine(); } } }
Результат выполнения программы такой.
Рис.1. Результат выполнения программы
В классе MyClass есть закрытый целочисленный массив nums. Массив создается и заполняется при вызове конструктора. Для заполнения массива запускается цикл. Там (при заданном индексе k) выполняется команда
this[k] = k + 1; ,
Инструкция this[k] означает, что индексируется объект, для которого вызван конструктор. Этому выражению присваивается значение k+1. Данная команда выполняется в соответствии с тем, как описан set-аксессор индексатора с целочисленным индексом (в классе описано несколько индексаторов). В соответствии с кодом set-аксессора выполняется команда
nums[k % nums.Length] = value; ,
Индексатор с целочисленным индексом мы используем и при переопределении метода ToString(): в теле метода при формировании текстовой строки (возвращаемой результатом метода) использовано выражение this[k], смысл которого такой же, как описывалось выше.
k == nums.Length - 1? "\n": " "
Но в классе MyClass описаны и другие индексаторы. Там есть индексатор с символьным индексом. В get-аксессоре результатом возвращается выражение this[s-'а']. Поскольку значением выражения s-'а' является число (разность кодов символов), то выражение this[s-'а'] вычисляется вызовом get-акссссора для индексатора с целочисленным индексом. Аналогично, в set-аксессоре для индексатора с символьным индексом при выполнении команды
this[s - 'a'] = value;
Индексатор с двумя индексами (целое число k и текст t) в get-аксессоре содержит команду
return this[t[k]]; .
this[t[k]] = value; .
Еще одна версия индексатора с двумя индексами отличается от описанной выше порядком индексов: теперь первый индекс t текстовый и второй индекс k целочисленный. В get-аксессоре выполняется команда
return this[k, t]; .
this[k, t] = value; ,
В главном методе программы создается объект obj класса MyClass. Массив в этом объекте содержит 6 (значение переменной n) элементов, заполненных числами от 1 до 6 включительно. Далее мы запускаем конструкцию цикла, в котором индексная переменная k "выскакивает" за допустимую верхнюю границу. Но проблемы в этом нет, поскольку при индексировании объекта obj выполняется циклическая перестановка индексов.
При выполнении команд
obj[1] = 7; obj[n + 3] = 8;
obj['a'] = 9; obj['k'] = 0;
Если мы используем символ в качестве индекса, то запрос выполняется для элемента с индексом, равным разности кодов этого символа и символа 'а'. Поэтому символ 'а' эквивалентен числовому индексу 0, а символ 'k' эквивалентен числовому индексу 10, что с учетом циклической перестановки для массива из шести элементов дает значение 4.
Когда мы перебираем элементы массива, индексируя символьными значениями объект, то при выходе за допустимую верхнюю границу индексы переставляются циклически - после значения последнего элемента массива отображается значение первого (начального) элемента, затем второго и так далее. Причина в том, что при обработке выражения с символьным индексом на самом деле вычисляется выражение с целочисленным индексом, а для таких случаев предусмотрена циклическая перестановка индексов.
При выполнении команды
obj[4, "alpha"] = 0;
obj["bravo", 0] = 6;
Также в главном методе есть конструкция цикла, в котором с помощью индексной переменной k перебираются символы из текстовой переменной txt (со значением "abc"). В консольном окне отображаются значения выражений вида obj[k, txt] и obj[txt, k], которые должны совпадать (и они совпадают), поскольку порядок следования индексов не имеет значения.
На следующем шаге мы резюмируем все ранее сказанное по данной теме на предыдущих шагах.