An indexer enables you to use an index on an object to obtain values stored within the object. In essence this enables you to treat an object like an array.
An indexer is also similar to a property. As with properties, you use get and set when defining an indexer. Unlike properties, you are not obtaining a specific data member; rather, you are obtaining a value from the object itself. When you define a property, you define a property name. With indexers, instead of creating a name as you do with properties, you use the this keyword, which refers to the object instance and thus the object name is used. The format for defining an indexer is
public dataType this[int index]
{
get
{
// Do whatever you want...
return aValue;
}
set
{
// Do whatever you want
// Generally you should set a value within the class
// based on the index and the value they assign.
}
}
Creating an indexer enables you to use bracket notation ([]) with an object to set and get a value from an object. As you can see in the format shown earlier, you state the dataType that will be set and returned by the indexer. In the get section, you return a value that is of dataType. In the set block, you will be able to something with a value of dataType. As with properties and member functions, you can use the value keyword. This is the value passed as the argument to the set routine. Listing 1 presents a simple example of using an indexer.
Listing 1. Using an Indexer
1: // indx2.cs - Using an indexer
2: //--------------------------------------------------
3:
4: using System;
5:
6: public class SpellingList
7: {
8: protected string[] words = new string[size];
9: static public int size = 10;
10:
11: public SpellingList()
12: {
13: for (int x = 0; x < size; x++ )
14: words[x] = String.Format("Word{0}", x);
15: }
16:
17: public string this[int index]
18: {
19: get
20: {
21: string tmp;
22:
23: if( index >= 0 && index <= size-1 )
24: tmp = words[index];
25: else
26: tmp = "";
27:
28: return ( tmp );
29: }
30: set
31: {
32: if( index >= 0 && index <= size-1 )
33: words[index] = value;
34: }
35: }
36: }
37:
38: public class TestApp
39: {
40: public static void Main()
41: {
42: SpellingList myList = new SpellingList();
43:
44: myList[3] = "=====";
45: myList[4] = "Brad";
46: myList[5] = "was";
47: myList[6] = "Here!";
48: myList[7] = "=====";
49:
50: for ( int x = 0; x < SpellingList.size; x++ )
51: Console.WriteLine(myList[x]);
52: }
53: }
The output from this listing is:
Word0
Word1
Word2
=====
Brad
was
Here!
=====
Word8
Word9
This listing creates an indexer to be used with the SpellingList class. The SpellingList class contains an array of strings called words that can be used to store a list of words. This list is set to the size of the variable declared in line 9.
Lines 11 to 15 contain a constructor for SpellingList that sets initial values into each element of the array. You could just as easily have requested the words from the reader or read them from a file. This constructor assigns the string value Word## to each of the elements in the array, where ## is the element number of the array.
Jumping down to the TestApp class in lines 38 to 53, you see how the SpellingList class is going to be used. In line 42, the SpellingList class is used to instantiate the myList object that will hold the words. Line 42 also causes the constructor to be executed. This initializes the Word## values. Lines 44 to 48 then change some of these values.
If you think back to how you worked with arrays, you should be saying, "wait a minute" as you look at lines 44 to 48. To access the value of one of the words, you would normally have to access the data member within the object. When using arrays as a data member, you learned that you would assign a value to the fourth element as
MyList.words[3] = "=====";
Line 44, however, is accessing the fourth element within the object, which has been set to be the fourth element in the words array.
An indexer has been created for the SpellingList class in lines 17 to 35. This indexer enables you to access the elements within the words array using just the object name.
Line 17 is the defining line for the indexer. You know this is an indexer rather than a property because the this keyword is used instead of a name. Additionally, this is given an index (called index). The indexer will return a string value.
Lines 19 to 29 contain the get portion of the indexer. The get block returns a value based on the index. In this class, this value is an element from the words array. You can return any value you want. The value should, however, make sense. In line 23, a check is done to make sure the index value is valid. If you don't check the value of the index, you risk having an exception thrown. In this listing, if the index is out of the range, a null value is returned (line 26). If the index is valid, the value stored in the words array at the index location will be returned.
The set portion of the indexer is in lines 30 to 34. This block can be used to set information within the object. As with properties, the value keyword contains the value being assigned. In this code, the index value is again checked to make sure it is valid. If it is, a word in the words array will be updated at the index location with the value assigned.
Looking again at the test class, you see that the set indexer block is used to assign values in lines 44 to 48. For line 44, the set indexer block will be called with value equal to ===== and will be passed with an index value of 3. In line 45, value will be Brad and the index is 4. In line 51, the get indexer block is called with an index value of x. The value returned will be the string value returned by the get indexer block.
There are times to use indexers and there are times to not use them. You should use indexers when it makes your code more readable and easier to understand. For example, you can create a stack that can have items placed on it and taken off of it. Using an indexer, you could access items within the stack without needing to remove items.