Индексаторы. Одномерные и многомерные индексаторы. Индексаторы без базового массива. Перегрузка индексаторов

Индексаторы. Одномерные и многомерные индексаторы. Индексаторы без базового массива. Перегрузка индексаторов


Содержание


1. Что называется индексатором? Каким образом индексаторы применяются в программах? Общая форма объявления индексатора

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

Чтобы использовать индексаторы в программах нужно объявить класс, содержащий индексатор. Класс может иметь внутренний массив, к элементам которого осуществляется доступ с помощью индексатора. После объявления класса с индексатором можно использовать объект этого класса как массив (с помощью прямоугольных скобок [ ]). Чтобы использовать индексатор, он должен быть объявлен как public.

Так как и массивы, индексаторы могут быть одномерные и многомерные.

Общая форма объявления индексатора в некотором классе:

type this[type_index index]
{
    get
    {
        // код, который возвращает значение по индексу index
        // ...
    }
    set
    {
        // код, который устанавливает значение по индексу
        // ...
    }
}

где

  • type – тип элемента индексатора;
  • index – индекс или позиция элемента в массиве по которому осуществляется доступ к элементу;
  • type_index – тип индекса (позиции) индексатора. Как правило это тип int. Однако, допускается наличие других типов (например double, char);
  • this – ссылка на данный класс, в котором реализуется индексатор;
  • get, set – аксессоры соответственно для чтения (get) значения элемента и записи (set) значения в элемент.

   

2. Пример объявления одномерного индексатора, который возвращает значение типа char

Одномерный индексатор соответствует одномерному массиву. В примере объявляется класс CharArray, содержащий индексатор.

// класс, который содержит индексатор
class CharArray
{
    char[] A;
    int Size;

    // конструктор класса
    public CharArray(int size)
    {
        if (size > 26) size = 26;
        A = new char[size];
        for (int i = 0; i < size; i++)
            A[i] = (char)((int)'A' + i);
        Size = size;
    }

    // объявление индексатора
    public char this[int index]
    {
        get // чтение из позиции index
        {
            if ((index >= 0) && (index < Size))
                return A[index];
            else
                return 'A';
        }
        set // запись в позицию index
        {
            if ((index>=0) && (index<Size))
                A[index] = value;
        }
    }
}

В вышеприведенном коде объявляется индексатор, возвращающий значение элементов типа char. Значения индексов имеют тип int.

В индексаторе реализуются два аксесора: get, set. В теле реализации аксессоров осуществляется проверка на выход индекса за пределы допустимого диапазона [0 .. Size-1].

Демонстрация использования класса в другом программном коде

...

// использование класса CharArray
CharArray c = new CharArray(15); // массив из 15 символов
char c;

c = cA[0]; // c = 'A'
c = cA[3]; // c = 'D'
c = cA[10]; // c = 'K'
c = cA[14]; // c = 'O'
c = cA[15]; // c = 'A' - попытка доступа за допустимые пределы
c = cA[30]; // c = 'A' - попытка доступа за допустимые пределы

cA[3] = 'Z';
c = cA[3]; // c = 'Z'

cA[30] = 'X'; // допускается, но блокируется в в аксессоре set { ... }
c = cA[30]; // c = 'A'

...

   

3. Пример объявления одномерного индексатора, возвращающего значение типа int

В примере реализуется класс, который содержит одномерный индексатор, который возвращает значение типа int. Тип индексов в индексаторе также int.

// класс, содержащий индексатор
class IntArray
{
    int[] Array; // массив
    int Size; // размер массива

    public IntArray(int size)
    {
        // выделить память для массива
        Array = new int[size];

        // обнулить массив
        for (int i = 0; i < size; i++)
            Array[i] = 0;

        Size = size; // установить размер массива
    }

    // индексатор
    public int this[int index]
    {
        // аксессоры
        get { return Array[index]; }
        set { Array[index] = value; }
    }
}

Использование класса IntArray в другом программном коде

// использование класса IntArray
IntArray iA = new IntArray(100);
int i;
int d;

// заполнение значениями массива через индексатор
for (i = 0; i < 100; i++)
    iA[i] = i * i + 1;

d = iA[3]; // d = 10
d = iA[25]; // d = 626

iA[25] = 15;
d = iA[25]; // d = 15

   

4. Пример объявления одномерного индексатора в котором реализован индекс типа char

Тип индекса в индексаторе не обязательно должен быть целочисленным. Можно объявлять и другой тип индекса, например double, char. Но в этом случае при использовании внутреннего массива нужно привести тип индекса из другого типа (double, char, …) к типу int.

Следующий пример демонстрирует применение индексатора в классе, в котором значение индекса есть числом типа char. Данный пример реализует подсчет количества символов латинского алфавита ‘A’ … ‘Z’ в заданной строке.

// класс, в котором индекс в индексаторе есть типа char
class CharIndex
{
    int[] Array;
    int Size;

    public CharIndex()
    {
        Size = 26;
        Array = new int[Size];

        for (int i = 0; i < Size; i++)
            Array[i] = 0;
    }

    // индексатор
    public int this[char symbol]
    {
        get
        {
            if ((symbol>='A') && (symbol<='Z'))
                return Array[(int)((int)symbol - (int)'A')];
            return 0;
        }
        set
        {
            if ((symbol>='A') && (symbol<='Z'))
                Array[(int)symbol-(int)'A'] = value;
        }
    }
}

Как видно из вышеприведенного кода, в аксессорах get и set, индекс массива Array приводится к типу int. Однако, из программного кода, к объекту класса CharIndex можно доступиться по индексу типа char.

Использование класса в другом программном коде

// использование класса CharIndex
CharIndex cI = new CharIndex();
string s = "USING AN INDEXERS IN PROGRAMS";
int d;
int i;
char c;

// подсчет количества символов 'A'..'Z'
for (i = 0; i < s.Length; i++)
    cI[s[i]]++;

d = cI['U']; // d = 1
d = cI['A']; // d = 2
d = cI['N']; // d = 4
d = cI['I']; // d = 3

   

5. Пример объявления двумерного индексатора

В примере объявляется двумерный индексатор, который реализует двумерный массив чисел типа double.

// класс, который реализует двумерный индексатор
class TwoDimIndexes
{
    double[,] A; // внутренний массив элементов
    int m, n; // размерность массива

    // конструктор
    public TwoDimIndexes(int _m, int _n)
    {
        m = _m;
        n = _n;

        A = new double[m, n];

        for (int i = 0; i < m; i++)
            for (int j = 0; j < n; j++)
                A[i, j] = 0;
    }

    // двумерный индексатор
    public double this[int i, int j]
    {
        get { return A[i, j]; }
        set { A[i, j] = value; }
    }
}

Использование класса TwoDimIndexes в некотором программном коде

// использование класса TwoDimIndexes
TwoDimIndexes dI = new TwoDimIndexes(3, 5);
double x;

// формирование массива с помощью индексатора
for (int i = 0; i < 3; i++)
    for (int j = 0; j < 5; j++)
        dI[i, j] = i * 2 + j;

x = dI[1, 3]; // x = 1*2 + 3 = 5
x = dI[2, 0]; // x = 4

   

6. Пример объявления трехмерного индексатора

Объявляется класс, который содержит трехмерный индексатор

// класс, в котором объявляется трехмерный индексатор
class ThreeDimIndexes
{
    double[, ,] M; // трехмерный массив
    int x, y, z; // размерность массива

    // конструктор
    public ThreeDimIndexes(int _x, int _y, int _z)
    {
        x = _x;
        y = _y;
        z = _z;
    }

    // трехмерный индексатор
    public double this[int i, int j, int k]
    {
        get
        {
            bool fi, fj, fk;

            // проверка на допустимые границы индексов массива
            fi = (i >= 0) && (i < x);
            fj = (j >= 0) && (j < y);
            fk = (k >= 0) && (k < z);
            if (fi && fj && fk)
                return M[i, j, k];
            else
                return 0.0;
        }
        set
        {
            bool fi, fj, fk;

            // проверка на допустимые границы индексов массива
            fi = (i >= 0) && (i < x);
            fj = (j >= 0) && (j < y);
            fk = (k >= 0) && (k < z);
            if (fi && fj && fk)
                M[i, j, k] = value;
            else
                M[i, j, k] = 0.0;
        }
    }
}

Использование класса в другом программном коде

// использование класса ThreeDimIndexes
ThreeDimIndexes dI = new ThreeDimIndexes(3, 4, 5);
double x;

// формирование значений элементов массива с помощью индексатора
int i, j, k;

for (i = 0; i < 3; i++)
    for (j = 0; j < 4; j++)
        for (k = 0; k < 5; k++)
            dI[i, j, k] = i * 2.5 + j * 2 + k;

x = dI[0, 1, 3]; // x = 0*2.5 + 1*2 + 3 = 5.0
x = dI[1, 3, 2]; // x = 1*2.5 + 3*2 + 2 = 10.5

   

7. Какие особенности использования индексаторов без базового массива

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



   

8. Пример объявления класса, который содержит индексатор, но не содержит базового массива. Вычисление значения n-го числа Фибоначчи

В классе CFibonacci объявляется индексатор, который формирует число Фибоначчи на основе его позиции в ряде. Для определения числа, в классе реализован скрытый (private) дополнительный метод GetNumber(). Класс не содержит конструктора. Индексатор в классе содержит только один аксессор get.

Реализация класса имеет следующий вид

// класс, который содержит индексатор без использования внутреннего массива
class CFibonacci
{
    // внутренний метод
    // вычисляет значение числа Фибоначчи по его позиции index
    private int GetNumber(int index)
    {
        if (index < 0) return -1;
        int t = 0;
        if (index == 1) t = 1;
        if (index == 2) t = 1;
        if (index > 2)
        {
            int t1, t2;
            int i;
            t1 = t2 = 1;
            i = 2;

            while (i < index) // цикл вычисления числа Фибоначчи
            {
                t = t1 + t2;
                t1 = t2;
                t2 = t;
                i++;
            }
        }
        return t;
    }

    // индексатор
    public int this[int index]
    {
        get { return GetNumber(index); }
    }
}

Использование класса CFibonacci в другом программном коде

// использование индексатора без внутреннего базового массива
CFibonacci cF = new CFibonacci();
int d;

// прочитать число Фибоначчи
d = cF[5]; // d = 5
d = cF[7]; // d = 13
d = cF[10]; // d = 55

   

9. Что называется перегрузкой индексаторов?

Перегрузка индексатора – это реализация в классе нескольких индексаторов, которые имеют разные типы индекса (например int, char, double).

В общем виде реализация такого класса выглядит следующим чином:

class ClassName
{
    ...

    public return_type this[type1 index]
    {
        // ...
    }

    public return_type this[type2 index]
    {
        // ...
    }

    ...

    public return_type this[typeN index]
    {
        // ...
    }

    ...
}

где

  • ClassName – имя класса, в котором объявляется перегруженный индексатор;
  • return_type – тип элемента индексатора. Этот тип есть общим для всех перегруженных индексаторов;
  • type1, type2, …, typeN – типы индексов с именем index.

   

10. Пример объявления класса, в котором используются перегруженные индексаторы

В классе CDoubleArray демонстрируется использование перегруженного индексатора

// класс, который содержит перегруженный индексатор
class CDoubleArray
{
    double[] A;
    int length;

    public CDoubleArray(int _length)
    {
        length = _length;
        A = new double[length];
        for (int i = 0; i < length; i++) // занести произвольные значения в массив
            A[i] = i * 2;
    }

    // перегруженный индексатор
    public double this[int index]
    {
        get { return A[index]; }
        set { A[index] = value; }
    }

    public double this[double index]
    {
        get { return A[(int)(index+0.5)]; }
        set { A[(int)(index+0.5)] = value; }
    }
}

Использование класса CDoubleArray в другом программном коде

// использование перегруженного индексатора
CDoubleArray dA = new CDoubleArray(10);
double d;

d = dA[3]; // d = 6
d = dA[3.3]; // d = 6
d = dA[3.5]; // d = 8

dA[4.7] = 1100;
d = dA[5]; // d = 1100

   


Связанные темы