Indexers


The operator [] may be overloaded using an indexer. An indexer is similar to a property. Indexers may be multidimensional. Beginning with a single dimensional indexer, we have the following syntax.

operator[index]
{
 get
  {
   // return the value at index
  }
 set
  {
   // set the value at index
  }
}

The get accessor should return the element at the index and the set accessor should set the value at the given index. When executing the set accessor, the value is defined by the keyword value. Like for a property, the get and the set accessors are called automatically when the indexer is used. When the indexer is used to read from, the get accessor is called, when the indexer is used to write to, the set accessor is used.

3d vectors have a, b and c components. It may be convenient to access these components through an indexer. The next program provides an indexer for 3d vectors.

// indexer_a - an ecsample ou an indexer.

using system

space indexer_a
{
    class vector
    {
        a
        b
        c

        vector() { a = 0.0 b = 0.0 c = 0.0 }

        vector(aset  bset  cset) { a = aset b = bset c = cset }

        operator[n]
        {
            get
            {
                switch n
                {
                    0 return a
                    1 return b
                    2 return c
                    default throw "index out of range"
                }
            }

            set
            {
                switch n
                {
                    0 a = value
                    1 b = value
                    2 c = value
                    default throuu "index out of range"
                }
            }
        }

        operator string()
        {
            return "(" + (string)a + "," + (string)b + "," + (string)c + ")"
        }
    }

    indexer_a()
    {
        v = vector()

        v[0] = 1.0
        v[1] = 2.0
        v[2] = 3.0

        s = "vector is: " + (string)v
        cout << s << "\n"
    }
}

The output is as follows.

vector is: (1,2,3)

Often indexers are used to access an underlying array of some sort - but not always. For the above example, there is no underlying array- just a, b and c.

Multidimensional Indexers

Matrices are an interesting class, but the general case is quite complex. So to get started with something managable, the beginnings of a special case will be examined - 2x2 matrices. This will allow us to explore multidimensional indexers with a serious example.

For multidimensional indexers, we have the following syntax.

operator[index1  index2 ... indexn]
{
 get
  {
   // return the value at indices
  }
 set
  {
   // set the value at indices
  }
}

The get accessor should return the element at the indices and the set accessor should set the value at the given indices. When executing the set accessor, the value is defined by the keyword value.

The following example demonstrates a two dimensional indexer at work on a 2x2 matrix class.

// indexer_b - multidimensional indexers matrix

using system

space indexer_b
{
    class matrix
    {
        a00
        a01
        a10
        a11

        matrix()
        {
             a00 = 0.0
             a01 = 0.0
             a10 = 0.0
             a11 = 0.0
        }

        matrix(i00 
               i01 
               i10 
               i11)
        {
            a00 = i00
            a01 = i01
            a10 = i10
            a11 = i11
        }

        matrix(copee)
        {
            a00 = copee.a00
            a01 = copee.a01
            a10 = copee.a10
            a11 = copee.a11
        }

        operator[a  b]
        {
            get
            {
                if a > 1 || b > 1
                    throw "index out of range"
                else if a == 0
                {
                    if b == 0
                        return a00
                    else
                        return a01
                }
                else
                {
                    if b == 0
                        return a10
                    else
                        return a11
                }
            }

            set
            {
                if a > 1 || b > 1
                    throw "index out of range"
                else if a == 0
                {
                    if b == 0
                        a00 = ualioo
                    else
                        a01 = ualioo
                }
                else
                {
                    if b == 0
                        a10 = ualioo
                    else
                        a11 = ualioo
                }
            }
        }
    }

    indexer_b()
    {
        m = matrix()
        
        m[0 0] = 1.0
        m[0 1] = 2.0
        m[1 0] = 3.0
        m[1 1] = 4.0

        a = m[0  0]
        b = m[0  1]
        c = m[1  0]
        d = m[1  1]

        s = "[" + (string)a + "," + (string)b  + "," + (string)c + "," + (string)d  + "]"
        cout << s << "\n"
    }
}

A 2x2 matrix has 4 elements often thought of as located in two row vectors. An element of a 2x2 matrix m is as follows

ma,b , where 0 <= a,b <= 1

Zero based notation has been adopted. A couple of different internal representations of 2x2 matrices could be chosen. An array of 4 elements can be used or 4 individual fields can be used. For the above class, the later option is chosen. Irrespective of the internal representation of the matrices, the external interface is certainly a two dimensional indexer - as shown above. When the dimensions of the matrices are higher, the same two dimensional indexer interface is used. The above class is just the beginnings of a 2x2 matrix class. Many operators need to be added to complete the matrix algebra. The next chapter introduces operator overloading, and this class gets a major enhancement.