What are named indexers?
Someone asked me about this, so I decided to write up the answer here in case other folks are interested.
Indexers are pretty well documented on the web; I’ll quote some here.
https://www.csharphelp.com/archives/archive140.html
C# introduces a new concept known as Indexers which are used for treating an object as an array. The indexers are usually known as smart arrays in C# community. Defining a C# indexer is much like defining properties. We can say that an indexer is a member that enables an object to be indexed in the same way as an array.
<modifier> <return type> this [argument list]
{
get
{
// Get codes goes here
}
set
{
// Set codes goes here
}
}
I don’t usually know them as “smart arrays” which must mean I’m not really part of the C# community. But I’m trying!
Or the official source: https://msdn.microsoft.com/library/en-us/csref/html/vclrfindexedpropertiespg.asp
Indexers allow you to index a class or a struct instance in the same way as an array.
Hmm, that’s better. Let’s have an example:
class MyCollection<T>
{
T this[uint i]
{
get
{
return //...
}
}
}
class C
{
static void Main(string[] args)
{
MyCollection<string> someStrings = ...;
// here we index into the object like we would into an array
Console.WriteLine(someStrings[1]);
}
}
On the language tools side, indexers are a little funky. They look a lot like methods (parameter list, return type, accessibility) and a lot like properties (separate ‘get’ and ‘set’ accessors) and a bit different from both (no name).
So, what are named indexers?
Well, regular indexers don’t have a name, you just use them. But what if there are two different ways to index in to your object? You could overload, if that makes sense for your domain:
T this[uint i] { get { /*...*/ } }
T this[string s] { get { /*...*/ } }
Or you could use my “named indexer pattern” to create the appearance of a named indexer. Here’s an example with 2 named indexers to give some context.
class Car
{
//
// wheels
//
object[] wheels;
public IIndexer<object, uint>.Get Wheels { get { return new WheelsHelper(this); } }
public struct WheelsHelper : IIndexer<object, uint>.Get
{
readonly Car _outer;
public WheelsHelper(Car mc) { this._outer = mc; }
object IIndexer<object, uint>.Get.this[uint index] { get { return this._outer.wheels[index]; } }
}
//
// seats
//
object[] seats;
public SeatsHelper Seats { get { return new SeatsHelper(this); } }
public struct SeatsHelper
{
readonly Car _outer;
public SeatsHelper(Car mc) { this._outer = mc; }
public object this[int index] { get { return this._outer.seats[index]; } }
}
}
Now you can just write ‘car.Seats[1]’ and it works.
BTW, to compile this code, you’ll also need this code, which I just made up. It’s an experiment; not sure if I like it or not.
static class IIndexer<T, I>
{
public interface Get { T this[I index] { get;} }
public interface Set { T this[I index] { set;} }
public interface Both : Get, Set { }
}