Developer

Stupid C# tricks: Working with indexers

One of the more interesting features of C# is the indexer. See how it enables you to treat a class like an array.


One of C#'s most interesting features is the class indexer. Simply put, an indexer is a special kind of property that allows you to reference your class like you would an array. Obviously, this ability becomes useful when creating a collection class, but giving a class array-like behavior can be useful in other situations, such as when dealing with a large file or abstracting a set of finite resources. In this article, I'll show you how to set up a class to use an indexer. But first, let's get some necessary background by reviewing the concept of properties.

Properties 101
If you've done any programming in VB6, you'll be familiar with property methods—special class members that allow controlled access to a private class field. In C#, you have two kinds of properties: get, which allows you to return the value of a private field, and set, which allows you to set the value of a private field. As a simple example, consider the code below, which creates a FirstName property to control access to the private class member firstname:
class Person {
private string firstname;
public string FirstName {
get {return firstname;}
set {firstname = value;}
}
}

That property declaration would allow you to write code like this:
Person p = new Person();
p.FirstName = "Lamont";
Console.WriteLine (p.FirstName);

As you can see, a property declaration looks a lot like a field declaration, except that it's declared with two special members, called accessors in Microsoft parlance. The get accessor is called when the property is invoked on the right-hand side of an expression or used as a parameter to another routine. The set accessor is called when the property is invoked on the left-hand side of an expression and sets the value of its associated private field via the implicitly passed value parameter. You'd create a read-only property by omitting the set accessor, which would cause any attempts to set the property to generate compiler errors.

Using indexers for fun and profit
So why did I launch myself on that tangent? Because a class indexer works almost exactly like a property and looks very similar when you examine the code. Here's a sample class with an indexer that returns a string:
class Sample {
public string this [int index] {
get {return "You passed " + index; }
)
}

Notice that the property name is this, which refers back to the current instance of the class, and that the parameter list is enclosed in square brackets instead of parentheses. Also, this is a read-only indexer. To make it read/write, I'd have to add a set accessor. When defining an indexer, you are not limited to a single parameter. Indexer parameters may be of any type, although int usually makes the most sense. It's also possible to have more than one indexer in the same class (overloading)—more on that later.

Having defined Sample in this way, we can use the indexer as a sort of default property, like so:
Sample s = new Sample();
Console.WriteLine(s[55]);

Properties vs. indexers
There are a few differences between properties and indexers. Essentially, they boil down to the following:
  • Each property in a class must have a unique name, while each indexer defined in a class must have a unique signature or parameter list. (This allows indexer overloading.)
  • Properties can be static, while indexers must always be instance members.
  • Accessors defined for an indexer can access parameters passed to the indexer, while property accessors have no parameters.

Advanced tricks: Interfaces
When array-like behavior is desired for an implementer, you can define indexers for interfaces. In fact, the IList and IDictionary collection interfaces both declare an indexer to provide access to the items they're storing.

When declaring an indexer for an interface, remember that the declaration just serves to indicate the existence of the indexer. You need only provide the appropriate accessors and should not include a scope modifier. The following is an example of an indexer declared as part of an interface appropriately named IImplementMe:
interface IImplementMe {
string this[int index]
{
get;
set;
}
 

An implementing class would have to provide the implementations for both the get and set accessors for IImplementMe's indexer.

That'll about do it for now. You should have a strong enough understanding of indexers to play with them on your own. Have fun.

Editor's Picks