На этом шаге мы рассмотрим еще один пример использования таких индексаторов.
Еще один пример использования двумерного индексатора представлен в программе ниже. В данном примере описывается класс MyClass, в котором имеется три массива (предполагается, что одного и того же размера): символьный, текстовый и целочисленный. С помощью данного класса мы в очень простом варианте реализуем конструкцию, напоминающую ассоциативный контейнер. В таком контейнере есть элементы, у каждого элемента имеется значение, а доступ к элементу осуществляется на основе ключей. Ключ во многом схож с индексом, но в отличие от индекса ключ не обязательно должен быть целочисленным. Мы будем исходить из того, что ключи элементов записываются в символьный и текстовый массивы, а значение элемента хранится в целочисленном массиве.
С помощью индексатора мы реализуем две операции:
При присваивании значения выражению (с проиндексированным объектом) выполняется поиск по индексам. Если элемент найден, то ему присваивается новое значение (точнее, в целочисленный массив для соответствующего элемента записывается новое значение). Если элемента с указанными индексами в объекте нет, то он добавляется в объект (каждый из трех массивов получает по одному новому элементу).
Рассмотрим представленный ниже программный код.
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace pr133_1 { // Класс с двумерным индексатором: class MyClass { // Закрытое поле, являющееся ссылкой на // целочисленный массив: private int[] vals; // Закрытое поле, являющееся ссылкой на // символьный массив: private char[] ckey; // Закрытое поле, являющееся ссылкой на // текстовый массив: private string[] skey; // Закрытый метод для добавления новых элементов // в массивы: private void add(char a, string b, int n){ // Локальная переменная для записи размера // новых массивов: int size; // Переменная для символьного массива: char[] s; // Переменная для текстового массива: string[] t; // Переменная для целочисленного массива: int[] v; // Определение размера новых массивов: if (vals == null) size = 1; // Если массиза нет else size = vals.Length + 1; // Если массив существует // Создание символьного массиза: s = new char[size]; // Значение последнего элемента созданного массива: s[s.Length - 1] = a; // Создание текстового массива: t = new string[size]; // Значение последнего элемента созданного массива: t[t.Length - 1] = b; // Создание целочисленного массива: v = new int[size]; // Значение последнего элемента созданного массива: v[v.Length - 1] = n; // Заполнение созданных массивов: for (int k = 0; k < size - 1; k++){ s[k] = ckey[k]; t[k] = skey[k]; v[k] = vals[k]; } // Новые значения полей: ckey = s; skey = t; vals = v; } // Переопределение метода ToString(): public override string ToString(){ // Текстовая переменная: string txt = "Содержимое объекта:\n"; // Если массив существует: if (vals != null) { // Перебор элементов массивов: for (int k = 0; k < ckey.Length; k++){ // Добавление текста к текущему значению: txt += ckey[k] + ": " + skey[k] + ": " + vals[k] + "\n"; } } else {// Если массива нет // Добавление текста к текущему значению: txt += "Пустой объект\n"; } // Результат метода: return txt; } // Целочисленный индексатор с двумя индексами // символьного и текстового типа: public int this[char a, string b]{ // Метод вызывается при считывании значения // выражения с проиндексированным объектом: get { // Если массив существует: if (vals != null) { // Перебор элементов массива: for (int k = 0; k < ckey.Length; k++){ // Если элемент найден: if (a == ckey[k] && b==skey[k]){ // Значение выражения: return vals[k]; } } } // Код выполняется, если массив не существует или // если элемент не найден: int res = 0; // Значение для нового элемента add(a, b, res); // Добавление нового элемента return res; // Значение выражения } // Метод вызывается при присваивании значения // выражению с проиндексированным объектом: set { // Если массив существует: if (vals != null) { // Перебираются элементы массива: for (int k = 0; k < ckey.Length; k++){ // Если элемент найден: if (a == ckey[k] && b == skey[k]) { // Присваивание значения элементу: vals[k] = value; // Завершение метода: return; } } } // Если массива нет или если элемент не найден: add(a, b, value); // Добавление нового элемента } } } // Класс с главным методом: class Program { // Главный метод: static void Main() { // Создание объекта: MyClass obj = new MyClass(); // Проверка содержимого объекта: Console.WriteLine(obj); // Проверка значения элемента: Console.WriteLine("Значение элемента: " + obj['А', "Первый"] + "\n"); // Проверка содержимого объекта: Console.WriteLine(obj); // Присваивание значения элементу: obj['В', "Второй"] = 200; // Присваивание значения элементу: obj['С', "Третий"] = 300; // Проверка содержимого объекта: Console.WriteLine(obj); // Проверка значения элемента: Console.WriteLine("Значение элемента: " + obj['В', "Первый"] + "\n"); // Проверка значения элемента: Console.WriteLine("Значение элемента: " + obj['В', "Второй"] + "\n"); // Проверка значения элемента: Console.WriteLine("Значение элемента: " + obj['А', "Третий"] + "\n"); // Проверка содержимого объекта: Console.WriteLine(obj); // Присваивание значения элементу: obj['А', "Первый"] = 100; // Проверка содержимого объекта: Console.WriteLine(obj); // Проверка значения элемента: Console.WriteLine("Значение элемента: " + obj['А', "Первый"] + "\n"); // Задержка: Console.ReadLine(); } } }
Результат выполнения программы представлен ниже.
Рис.1. Результат выполнения программы
В классе MyClass, как отмечалось, есть три закрытых массива:
s[s.Length - 1] = a; t[t.Length - 1] = b; v[v.Length - 1] = n;
s[k] = ckey[k]; t[k] = skey[k]; v[k] = vals[k];
ckey = s; skey = t; vals = v;
Метод ToString() переопределен таким образом, что если поля объекта на массивы не ссылаются, то возвращается текстовая строка с информацией о том, что объект пустой. Если массивы существуют, то возвращается текстовая строка со значениями элементов трех массивов объекта (в одной строке значения элементов с одним и тем же индексом).
В get-аксессоре индексатора проверяется условие vals!=null. Истинность условия означает, что целочисленный массив существует (а значит, и два других массива тоже). В этом случае синхронно перебираются все элементы символьного и текстового массивов. Для каждого индекса к проверяется условие
if (a == ckey[k] && b==skey[k]){
В случае, если массивы не существуют или если совпадение не найдено, вызывается метод add(), которым в массивы добавляется по новому элементу (или создаются массивы, если их не было), и значение (нулевое) элемента, добавленного в целочисленный массив, возвращается результатом выражения.
В set-аксессоре выполняются аналогичные действия, но последствия немного другие. Если при просмотре содержимого массивов совпадение найдено (истинно условие
if (a == ckey[k] && b == skey[k]) {
vals[k] = value;
Если совпадение не найдено, то с помощью метода add() в массивы добавляется по одному новому элементу.
В главном методе создается объект obj класса MyClass. Проверка показывает (команда
Console.WriteLine(obj);
При выполнении команд
// Присваивание значения элементу: obj['В', "Второй"] = 200; // Присваивание значения элементу: obj['С', "Третий"] = 300;
Затем последовательно считываются значения выражений obj['В', "Первый"], obj['В', "Второй"] и obj['А', "Третий"]. Для первого и третьего из этих выражений в объекте obj совпадений индексов не обнаруживается, а для второго выражения совпадение есть. Поэтому для второго выражения возвращается значение 200 из целочисленного массива, а в соответствии с первым и третьим выражениями в массивы объекта добавляются новые элементы (в том числе нулевые значения в целочисленный массив).
Командой
// Присваивание значения элементу: obj['А', "Первый"] = 100;
На следующем шаге мы рассмотрим многомерные индексаторы.