Providing an Index Operator


The common collection interfaces provide much of the foundation for what members are needed when implementing custom collections. However, there is one more member: the index operator.

The index operator is a pair of square brackets that are generally used to index into a collection. Not only is this available on each collection type, but it is also a member that programmers can add to their custom classes. Listing 12.15 shows an example using Pair<T>.

Listing 12.15. Defining an Index Operator

    interface IPair<T>     {  T First  {  get;  }  T Second  {  get;  }   T this[PairItem index]                                               {                                                                    get;                                                                 }                                                                      }     public enum PairItem     {  First,  Second     }     public struct Pair<T> : IPair<T>     {  public Pair(T first, T second)  {  _first = first;  _second = second;  }  public T First  {  get{ return _first; }  private set{ _first = value;  }     }     private T _first;     public T Second     {  get{ return _second; }  private set{ _second = value;    }   }      private T _second;      public T this[PairItem index]                                                {                                                                            get                                                                          {                                                                             switch (index)      {  case PairItem.First:  return First;  case PairItem.Second:  return Second;  default :  throw new NotImplementedException(  string.Format(  "The enum {0} has not been implemented",  index.ToString()));     }   }       set                                                                         {                                                                      switch (index)  {  case PairItem.First:  First = value;  break;  case PairItem.Second:  Second = value;  break;  default:  throw new NotImplementedException(  string.Format(  "The enum {0} has not been implemented",  index.ToString()));        }      }                                                                        }                                                                         }  

To define an index operator, you must name the member this and follow it with square brackets that identify the parameters. The implementation is like a property with get and set blocks. As Listing 12.15 shows, the parameter does not have to be an int, and in fact, the index can take multiple parameters and even be overloaded. This example uses an enum to reduce the likelihood that callers will supply an index for a nonexistent item.

The resulting CIL code the C# compiler creates from an index operator is a special property called Item that takes an argument. Properties that accept arguments cannot be created explicitly in C#, so the Item property is unique in this aspect. This is because any additional member with the identifier Item, even if it has an entirely different signature, will conflict with the compiler-created member, and will therefore not be allowed.

Advanced Topic: Assigning the Indexer Property Name Using IndexerName

As indicated earlier, the CIL property name for an indexer defaults to Item. Using the IndexerNameAttibute you can specify a different name, however. Listing 12.16, for example, changes the name to "Entry".

Listing 12.16. Changing the Indexer's Default Name

 [System.Runtime.CompilerServices.IndexerName("Entry")]     public T this[params PairItem[] branches]     {  // ...     }  

This makes no difference to C# callers of the index, but it specifies the name for languages that do not support indexers directly.

Compilers consume this attribute and modify the generated CIL code. The attribute itself does not appear in the CIL output, and therefore, it is not available via reflection.


Advanced Topic: Defining an Index Operator with Variable Parameters

An index operator can also take a variable parameter list. For example, Listing 12.17 defines an index operator for BinaryTree<T> discussed in Chapter 11 (and again in the next section).

Listing 12.17. Defining an Index Operator with Variable Parameters

    using System;     using System.Collections.Generic;     public class BinaryTree<T>:     IEnumerable<T>     {   // ...      public T this[params PairItem[] branches]                    {  get  {  BinaryTree<T> currentNode = this;  int totalLevels =  (branches == null) ? 0 : branches.Length;  int currentLevel = 0;  while (currentLevel < totalLevels)  {  currentNode = currentNode.SubItems[  branches[currentLevel]];  if (currentNode == null)  { // The binary tree at this location is null.  throw new IndexOutOfRangeException();  }  currentLevel++;  }  return currentNode.Value;  }  set  {  // ...  }     }        }     }  

Each item within branches is a PairItem and indicates which branch to navigate down in the binary tree.





Essential C# 2.0
Essential C# 2.0
ISBN: 0321150775
EAN: 2147483647
Year: 2007
Pages: 185

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net