C# Indexers

If a non-static class or struct contains a data member which is an array or something we want to be able to deal with like an array, we need some way to access the different items in the array. This is called an "indexer".

The declaration of an indexer on a class or struct is of this form:

     access-modifier this[type parameter] {}

where the access modifier is public, private, etc.

Notice that it does not have a name in the usual sense, as it is indicated by the use of brackets ([]).

An indexer is an instance member of the class, not a static member. It is identifiable as an indexer by the use of "this".

In some ways, an indexer resembles a property. It can have get and set accessors (and that's all). It might have only get and not set.

In theory, an indexer can have more than one argument, but this is unusual. We might need it if we had a two-dimensional array, for instance.


Example:

     public class IntList
     {
      int T[100];
 
      // This is the indexer.  It is very simple.
      public int this[int I]
      {
       get
       { 
        return T[I];
       }
       set
       {
        T[I] = value;
       }
      }
     }

Later on, when we have

     IntList MyInts;

we can use [] with MyInts:

     MyInts[3] = 8;
     int J = MyInts[5] + 2;

It is worth noticing that the subscript value I might be out of range, in which case a System.IndexOutOfRange exception would be thrown.


The argument of an indexer does not have to be an integer. It could be a value that is in one of the array entries. In that case, the indexer will return the subscript where the input value was found.

Example, taken from an msdn web page:

// Using a string as an indexer value
     class DayCollection
     {
      string[] days = { "Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat" };

      // This method finds the day or throws an exception; notice it is private.
      private int GetDay(string testDay)
      {
       for (int j = 0; j < days.Length; j++)
        {
         if (days[j] == testDay)
         {
          return j;
         }
        }

       // Notice that the throw occurs only if testDay is not found.
       // Here the exception constructor has 2 arguments:  the name of
       // the argument and an error message.
       throw new System.ArgumentOutOfRangeException(testDay, 
           "testDay must be in the form \"Sun\", \"Mon\", etc");
      }

      // The get accessor returns an integer for a given string.
       public int this[string day]
      {
       get
       {
        return (GetDay(day));
       }
      }
     }

     class Program
     {
      static void Main(string[] args)
      {
       DayCollection week = new DayCollection();
       System.Console.WriteLine(week["Fri"]);

       // This next one raises an ArgumentOutOfRangeException
       System.Console.WriteLine(week["Made-up Day"]);

     // Keep the console window open in debug mode.
       System.Console.WriteLine("Press any key to exit.");
       System.Console.ReadKey();
      }
     }

     // Output: 5

In this example, we could if we liked also have a "catch" block for the exception.

Indexers can be overloaded. We could have both kinds of indexers (one to return the value, given the subscript, and one to return the subscript, given the value) for the DayCollection class.


Indexers and Interfaces

An indexer can be declared in an interface. It would look like this:

     public interface ISomeInterface
     {
      //...

      // Indexer declaration:
      sometype this[int index]
      {
       get;
       set;
      }
     }

Any class or struct implementing the interface will have to define a matching indexer.

Notice that the interface accessors do not have access modifiers or bodies of code. (As usual in an interface, we have the skeletons only.)