Indexers. One-dimensional and multidimensional indexers. Indexers without basic array. Overloading Indexers

Indexers. One-dimensional and multidimensional indexers. Indexers without basic array. Overloading Indexers


Contents


1. What is called an indexer? How are indexers used in programs? General form of the indexer declaration

The indexer is a tool of the C# language, which allows you to index the object as an array using rectangular brackets [ ].With the help of indexers, you can implement your own specialized arrays, which can be subject to various restrictions.

The indexers can be one-dimensional and multidimensional.

The general form of the indexer declaration in a certain class:

type this[type_index index]
{
   get
   {
       // code that returns a value by index
       // ...
   }
   set
   {
       // code that sets a value by index
       // ...
   }
}

where

  • type – the type of indexer item;
  • index – the index or the position of the item in array by which the item is accessed;
  • type_index – type index (position) of the indexer. Typically this is int type. However, other types are allowed (for example, double, char);
  • this – a reference to the class that implements the indexer;
  • get, set – the accessors respectively for reading (get) the value of the item and the save (set) value in the item.

 

2. Example of declaring a one-dimensional indexer that returns a value of type char

A one-dimensional indexer corresponds to a one-dimensional array. The example declares the class CharArray containing the indexer.

// class that contains the indexer
class CharArray
{
    char[] A;
    int Size;

    // constructor
    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;
    }

    // implementation of indexer
    public char this[int index]
    {
        get // read at 'index' position
        {
            if ((index >= 0) && (index < Size))
                return A[index];
            else
                return 'A';
        }
        set // save to 'index' position
        {
            if ((index>=0) && (index<Size))
                A[index] = value;
        }
    }
}

In the above code, an indexer is declared that returns the value of items of type char. Indexes values are of type int.

In the indexer, two accessors are implemented: get, set. In the body of the implementation of the accessors the correct indexes range is checked.

Using the class in another program code

...

// using CharArray class
CharArray c = new CharArray(15); // array of 15 characters
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' - access attempt beyond limits
c = cA[30]; // c = 'A' - access attempt beyond limits

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

cA[30] = 'X'; // it is allowed, but is blocked in the set {...}
c = cA[30]; // c = 'A'

...

 

3. Example of declaring a one-dimensional indexer that returns an int value

The example implements a class that contains a one-dimensional indexer that returns a value of type int. The index type in the indexer is also int.

// class containing the indexer
class IntArray
{
    int[] Array; // array
    int Size; // size of array

    public IntArray(int size)
    {
        // allocate memory for an array
        Array = new int[size];

        // zeroing of array
        for (int i = 0; i < size; i++)
            Array[i] = 0;

        Size = size; // set the size of array
    }

    // indexer
    public int this[int index]
    {
        // accessors
        get { return Array[index]; }
        set { Array[index] = value; }
    }
}

Using the IntArray class in another program code

// using the IntArray class
IntArray iA = new IntArray(100);
int i;
int d;

// filling the array values via the indexer
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. An example of declaring a one-dimensional indexer in which an index takes the char type

The index type in the indexer does not have to be an integer. You can also declare another type of index, for example double, char. But in this case, when using an internal array, you need to cast the index type to type int.

The following example demonstrates the use of the indexer in a class where the index value is a number of ‘char’ type. This example implements the calculation of the number of Latin characters ‘A’ .. ‘Z’ in a given string.

// class in which the index in the indexer is of type 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;
    }

    // indexer
    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;
        }
    }
}

As can be seen from the above code, in the get and set accessors, the Array index is cast to int type. However, from the program code, to the object of the class CharIndex you can access the index of the type ‘char’.

Using the class in another program code

// using the CharIndex class
CharIndex cI = new CharIndex();
string s = "USING AN INDEXERS IN PROGRAMS";
int d;
int i;
char c;

// counting the number of characters '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. Example of declaring a two-dimensional indexer

In the example, a two-dimensional indexer is declared, which implements a two-dimensional array of double numbers.

// class that implements a two-dimensional indexer
class TwoDimIndexes
{
    double[,] A; // internal array
    int m, n; // array dimension

    // constructor
    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;
    }

    // two-dimensional indexer
    public double this[int i, int j]
    {
        get { return A[i, j]; }
        set { A[i, j] = value; }
    }
}

Using the TwoDimIndexes class in some program code

// using of TwoDimIndexes class
TwoDimIndexes dI = new TwoDimIndexes(3, 5);
double x;

// array formation with indexer
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. Example of declaring a three-dimensional indexer

A class that contains a three-dimensional indexer is declared

// class in which a three-dimensional indexer is declared
class ThreeDimIndexes
{
    double[, ,] M; // three-dimensional array
    int x, y, z; // array dimension

    // constructor
    public ThreeDimIndexes(int _x, int _y, int _z)
    {
        x = _x;
        y = _y;
        z = _z;
    }

    // three dimensional indexer
    public double this[int i, int j, int k]
    {
        get
        {
            bool fi, fj, fk;

            // checking for valid array index bounds
            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;
        }
    }
}

Using a class in another program code

// using the ThreeDimIndexes class
ThreeDimIndexes dI = new ThreeDimIndexes(3, 4, 5);
double x;

// generation of values of elements of an array with the help of an indexer
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. What are the features of using indexers without a base array

In a class, you do not need to declare an internal array variable in order to use the indexer. Of course, the data can also be stored in an internal variable (array). However, there are cases when the value of the elements of the array are formed programmatically, that is, they are amenable to some regularity or formula. In these cases, it is not necessary to declare an internal array, for which additional memory is needed.

 

8. An example of a declaration a class that contains an indexer, but does not contain a base array. Calculating the value of the n-th Fibonacci number

In the CFibonacci class, an indexer is declared that forms the Fibonacci number based on its position in the series. To determine the number, the class implements the private extra method GetNumber(). The class does not contain a constructor. The indexer in the class contains only one get accessor.

Implementation of the class is as follows

// A class that contains an indexer without using an internal array
class CFibonacci
{
    // internal method
    // calculates the value of the Fibonacci number at its 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) // Fibonacci number calculation cycle
            {
                t = t1 + t2;
                t1 = t2;
                t2 = t;
                i++;
            }
        }

        return t;
    }

    // indexer
    public int this[int index]
    {
        get { return GetNumber(index); }
    }
}

Using the CFibonacci class in another program code

// using an indexer without an internal base array
CFibonacci cF = new CFibonacci();
int d;

// read the Fibonacci number
d = cF[5]; // d = 5
d = cF[7]; // d = 13
d = cF[10]; // d = 55

 

9. What is called an overload of indexers?

Overloading the indexer is the implementation of several indexers in the class that have different index types (for example, int, char, double).

In general, the implementation of this class looks like this:

class ClassName
{
    ...

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

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

    ...

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

    ...
}

where

  • ClassName – the name of the class in which the overloaded indexer is declared;
  • return_type – the type of the indexer item. This type is common to all overloaded indexers;
  • type1, type2, …, typeN – the types of indexes named ‘index’.

 

10. Example of a class declaration that uses overloaded indexers

The CDoubleArray class demonstrates the use of an overloaded indexer

// A class that contains an overloaded indexer
class CDoubleArray
{
    double[] A;
    int length;

    public CDoubleArray(int _length)
    {
        length = _length;
        A = new double[length];
        for (int i = 0; i < length; i++) // enter the arbitrary values into array
            A[i] = i * 2;
    }

    // overloaded indexer
    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; }
    }
}

Using the CDoubleArray class in another program code

// using an overloaded indexer
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


Related topics