Индексаторы

46

Программисты хорошо знакомы с процессом доступа к индивидуальным элементам, содержащимся в стандартных массивах, через операцию индекса ([]). В 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("{0}\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();
        }
    }
}
Многомерный индексатор C#
Пройди тесты
Лучший чат для C# программистов