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 { }

}