Опубликован: 25.03.2010 | Уровень: для всех | Доступ: платный
Лекция 5:

Контролирующий код C#

< Лекция 4 || Лекция 5: 123 || Лекция 6 >

Индексаторы

Создание объектов с контролирующим кодом, имитирующих работу массивов, является главной целью индексаторов. Существующий массив может упаковываться в поле класса, а один из общедоступных методов класса, который и называется индексатором, допускает форму вызова через объект класса по синтаксису массива. Индексаторы, как и массивы, бывают одномерными и многомерными.

Одномерный индексатор задается по следующему синтаксису:

// Одномерный индексатор - специфический метод класса
        тип_элемента this[int индекс]
        {
            // Аксессор чтения
            get
            {
                // Возврат значения, 
                // заданного элементом "индекс"
            }
            // Аксессор записи
            set
            {
                // Установка значения для элемента,
                // адресуемого с помощью "индекс"
            }
        }

Тип_элемента устанавливает тип индексируемых элементов, доступ к которым предоставляет индексатор. При использовании индексатора аксессоры вызываются автоматически и могут использовать значение индекс. Если индексатор стоит слева от оператора присваивания, то срабатывает аксессор set, которому передается в переменной value значение выражения для присваивания, стоящего справа. Если индексатор стоит справа от оператора присваивания, то срабатывает аксессор get, который должен вернуть какое-то значение переменной, стоящей слева. Фактически это то же самое, что и функции доступа, только используется синтаксис массивов.

Многомерный индексатор отличается от одномерного только тем, что вместо единственного индекса имеет список индексов, например, три индекса. Индексаторы должны быть объявлены с модификатором publi c, чтобы быть доступными во внешнем коде.

// Многомерный индексатор
        тип_элемента this[int индекс1, int index2, int index3]
        {
            // Аксессор чтения
            get
            {
                // Что-то делаем, используя индексы,
        // и возвращаем единственное значение
            }
            // Аксессор записи
            set
            {
                // Что-то делаем, используя индексы
            }
        }
// Файл Program.cs
using System;
    
class MyClass
{
    public MyClass()
    {
        // Настройка консоли
        Console.Title = "Использование индексаторов";
        Console.ForegroundColor = ConsoleColor.White;
        Console.CursorVisible = false;
    
        // Создаем объект класса, инкапсулирующего целочисленный массив
        const int SIZE = 4;
        ControlBordersArray arrayControl = new ControlBordersArray(SIZE);
        // Наполняем значениями
        for (int i = 0; i < arrayControl.Length; i++)
            arrayControl[i] = i + 1;
    
        // Печатаем исходный
        Console.WriteLine("Исходный массив:");
        for (int i = 0; i < arrayControl.Length; i++)
            Console.Write("{0, -3}", arrayControl[i]);
        // Перевод на новую строку и пустая строка
        Console.WriteLine(Environment.NewLine); 
    
        // Адресуемся за границы внутреннего массива
        arrayControl[-5] = 20;
        arrayControl[5] = 30;
    
        // Опять печатаем
        Console.WriteLine("Измененный массив:");
        for (int i = 0; i < arrayControl.Length; i++)
            Console.Write("{0, -3}", arrayControl[i]);
    }
}
    
// Упаковка массива в класс
class ControlBordersArray
{
    // Закрытые поля класса
    int[] array; // Ссылка на одномерный массив
    int length;  // Длина массива
    
    // Конструктор с параметрами
    public ControlBordersArray(int size)
    {
        length = size;         // Установили параметр длины
        array = new int[length]; // Создали одномерный массив
    }
    
    // Конструктор по умолчанию
    public ControlBordersArray()
    {
        length = 16;         // Установили параметр длины
        array = new int[length]; // Создали одномерный массив
    }
    
    // Публичное свойство только для чтения
    public int Length
    {
        get { return length; }
    }
    
    // Публичный метод - индексатор
    // При выходе индекса за границы работать с пограничными элементами
    public int this[int index]
    {
        get
        {
            if (index < 0)
                return array[0];
            else if (index >= length)
                return array[length - 1];
            else
                return array[index];
        }
        set
        {
            if (index < 0)
                array[0] = value;
            else if (index >= length)
                array[length - 1] = value;
            else
                array[index] = value;
        }
    }
}
    
class Program
{
    static void Main()
    {
        new MyClass();// Чтобы сработал конструктор
        // Для задержки консольного окна
        Console.ReadLine();
    }
}

Приведенный пример демонстрирует создание контролирующего границы кода при работе с элементами упакованного в класс массива. Но базового массива внутри класса может и не быть, а значения якобы хранящихся элементов массива фактически будут при каждом обращении вычисляться на лету. Конечно, индексатор для этого случая должен использоваться только для чтения без аксессора set.

В качестве примера приведем класс с двумя перегруженными индексаторами, один из которых на лету генерирует элементы таблицы умножения, другой - квадраты чисел. Какой индексатор присоединить в вызывающем коде, решает компилятор по сигнатуре индексатора (в нашем случае - по числу параметров). Также приведем метод, функциональность которого эквивалентна индексатору, только его применение построено в ином - традиционном синтаксисе.

// Файл Program.cs
namespace Test
{
    using System; // Инструкция может быть и внутри пространства имен
    
    class MyClass
    {
        public MyClass()
        {
            // Настройка консоли
            Console.Title = "Использование индексаторов";
            Console.ForegroundColor = ConsoleColor.White;
            Console.CursorVisible = false;
    
            // Создаем объект таблицы умножения
            MultiplicationTable table = new MultiplicationTable();
            string str;
    
            // Используем многомерный индексатор 
            // и распечатываем таблицу умножения
            Console.WriteLine("Таблица умножения по индексатору");
            for (int i = table.MinValue; i <= table.MaxValue; i++)
            {
                str = "";
                for (int j = table.MinValue; j <= table.MaxValue; j++)
                    str += String.Format("{0}x{1}={2, -3}  
                      ", i, j, table[i, j]);
                Console.WriteLine(str);
            }
    
            // Используем перегруженный одномерный индексатор 
            // и распечатываем квадраты чисел
            Console.WriteLine(Environment.NewLine +
                "Квадраты первых чисел натурального ряда");
                str = "";
                for (int i = table.MinValue; i <= table.MaxValue; i++)
                    str += String.Format("{0}x{0}={1, -3}  
                      ", i, table[i]);
                Console.WriteLine(str);
    
            // Используем метод и распечатываем таблицу умножения
            Console.WriteLine("\r\nТаблица умножения по методу");
            for (int i = table.MinValue; i <= table.MaxValue; i++)
            {
                str = "";
                for (int j = table.MinValue; j <= table.MaxValue; j++)
                    str += String.Format("{0}x{1}={2, -3}  ",
                            i, j, table.Get(i, j));
                Console.WriteLine(str);
            }
        }
    }
    
    // Имитация таблицы умножения
    class MultiplicationTable
    {
        // Свойства
        public int MinValue
        {
            get { return 1; }
        }
    
        public int MaxValue
        {
            get { return 9; }
        }
    
        // Публичный индексатор
        // имитирующий элементы таблицы умножения
        public int this[int index1, int index2]
        {
            get
            {
            int indexMin = Math.Min(index1, index2);
            int indexMax = Math.Max(index1, index2);
            if (indexMin >= MinValue && indexMax <= MaxValue)
               return index1 * index2;
            else
            {
            Console.WriteLine("Это уже высшая математика");
            return 0;
            }
            }
        }
    
        // Обычный метод
        // Код точно такой, что и в многомерном индексаторе
        public int Get(int index1, int index2)
        {
            int indexMin = Math.Min(index1, index2);
            int indexMax = Math.Max(index1, index2);
            if (indexMin >= MinValue && indexMax <= MaxValue)
                return index1 * index2;
            else
            {
                Console.WriteLine("Это уже высшая математика");
                return 0;
            }
        }
    
        // Перегруженный индексатор, возвращающий квадраты чисел
        public int this[int index]
        {
            get
            {
            if (index >= MinValue && index <= MaxValue)
                return index * index;
            else
            {
            Console.WriteLine("Это уже высшая математика");
            return -1;
            }
            }
        }
    }
    
    class Program
    {
        static void Main()
        {
            new MyClass();// Чтобы сработал конструктор
            // Для задержки консольного окна
            Console.ReadLine();
        }
    }
}

< Лекция 4 || Лекция 5: 123 || Лекция 6 >
Максим Филатов
Максим Филатов

Прошел курс. Получил код Dreamspark. Ввожу код на сайте, пишет:

Срок действия этого кода проверки уже истек. Проверьте, правильно ли введен код. У вас осталось две попытки. Вы также можете выбрать другой способ проверки или предоставить соответствующие документы, подтверждающие ваш академический статус.

 

Как активировать код?

Денис Пашков
Денис Пашков
Россия
Татьяна Ковалюк
Татьяна Ковалюк
Украина, Киев, Киевский политехнический институт, 1974