Індексатори. Одновимірні та багатовимірні індексатори. Індексатори без базового масиву. Перевантаження індексаторів

Індексатори. Одновимірні та багатовимірні індексатори. Індексатори без базового масиву. Перевантаження індексаторів


Зміст



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 cA = 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. Приклад оголошення класу, що містить індексатор, але не містить базового масиву

У класі 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

 


Зв’язані теми