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 example of an indexer.

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
            {
                case 0 return a
                case 1 return b
                case 2 return c
                default throw "index out of range"
            }
        }

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

    to_string()
    {
        return "(" + a.to_string() + "," + b.to_string() + "," + c.to_string() + ")"
    }
}

class indexer_a
{
    indexer_a()
    {
        u = new vector()

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

        s = "vector is: " + u.to_string()
        s.println()

        u[0] = u[2] + 1.0

        s = "vector is: " + u.to_string()
        s.println()
    }
}

The output is as follows.

vector is: (1,2,3)
vector is: (4,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 matrix22

class matrix22
{
    a00
    a01
    a10
    a11

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

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

    matrix22(copy)
    {
        a00 = copy.a00
        a01 = copy.a01
        a10 = copy.a10
        a11 = copy.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 = value
                else
                    a01 = value
            }
            else
            {
                if b == 0
                    a10 = value
                else
                    a11 = value
            }
        }
    }
}

class indexer_b
{
    indexer_b()
    {
        c = new matrix22()
        
        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 = "[" + a.to_string() + "," + b.to_string()  + "," + c.to_string() + "," + d.to_string()  + "]"
        s.println()
    }
}

A 2x2 matrix has 4 elements often thought of as located in to 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.