A way to do named indexers.

Published 16 July 04 12:49 PM

In this entry on Ladybug, a customer asked for named indexers in C#.

 

Here’s one way to get it:

 

      class Car

      {

            object[] wheels;

 

            public WheelsHelper Wheels { get { return new WheelsHelper(this); } }

 

            public class WheelsHelper

            {

                  readonly Car _outer;

 

                  public WheelsHelper(Car mc) { this._outer = mc; }

 

                  public object this[int index] { get { return this._outer.wheels[index]; } }

 

            }

      }

 

You use it in the obvious way:

 

            Car car = ...;

            car.Wheels[0];

 

 

 

Comments

# Sean said on July 16, 2004 1:27 PM:
meh.. the provided solution on msdn for 2.0 is a bit twacky also imho..

public class Car
{
protected List<Wheel> _wheels = new List<Wheel>();
protected List<Seat> _seats = new List<Seat>();

public List<Wheel> Wheels
{
get { return _wheels; }
}

public List<Seat> Seats
{
get { return _seats; }
}
}

Car.Wheels[0]; // Returns the wheel at index 0
Car.Seats[0]; // Returns the seat at index 0

Can implement this pattern in 1.0 also similarly to what you have done

public class Car
{
protected ArrayList _wheels = new ArrayList();
protected ArrayList _seats = new ArrayList();

public ArrayList Wheels
{
get { return _wheels; }
}

public ArrayList Seats
{
get { return _seats; }
}
}

or for 1.0 create your own WheelCollection and SeatCollection classes...

public WheelCollection : ICollection {...} // implements public Wheel this[int index];
# Sean said on July 16, 2004 1:51 PM:
um.. duh?

class Car
{
object[] wheels;
object[] seats;

public object[] Wheels
{
get { return wheels; }
}

public object[] Seats
{
get { return seats; }
}
}
# Sean said on July 16, 2004 1:51 PM:
what am I missing?
# jaybaz [MS] said on July 16, 2004 1:57 PM:
Mutability. Law of Demeter. Encapsulation. Verbosity.
# Nicholas Allen said on July 16, 2004 2:29 PM:
You're violating Demeter as well by operating on what was returned by Wheels. But mutability is probably the biggest concern because it doesn't appear that you intended for these to be editable.

I think the helper solution is not bad except:

1) A new helper is created every time you access Wheels. Since they should be immutable it would be better to share them.

2) The namespace gets polluted with this extra public class.
# jaybaz [MS] said on July 16, 2004 2:43 PM:
By the way, "Verbosity." is a joke. Get it?
# jaybaz [MS] said on July 16, 2004 2:46 PM:
1. This is the simplest way I know to write the code. If you take this code, measure your performance, and optimize as needed.

"Make it work, make it right, make it fast"

2. Well, kinda. It's a nested class, so it only shows up as a member of 'Car'.

I'm a big fan of nested classes.

http://blogs.msdn.com/jaybaz_ms/archive/2004/07/02.aspx
http://blogs.msdn.com/jaybaz_ms/archive/2004/04/28/122401.aspx#126071
http://blogs.msdn.com/jaybaz_ms/archive/2004/02/20/77385.aspx
# Christoffer Skjoldborg said on July 16, 2004 3:26 PM:
Isn't that what the System.Runtime.CompilerServices.IndexerName does?
# Nicholas Allen said on July 16, 2004 3:35 PM:
I'm a big fan of nested classes too. I just wish there was a good way to hide one in this case. If there had already been some public class or interface that worked here, we could have declared the return type to be that class and made the inner class implementation private. Maybe it just needs to be stealthy enough for Intellisense not to pick up it.
# Nicholas Allen said on July 16, 2004 4:15 PM:
Singletons aren't just for performance. Someone using this is going to have the possibly unexpected behavior that car.Wheels == car.Wheels is false. You can't use it in a data structure that relies on searching. It's possible to implement Equals and GetHashCode to avoid this but that's more work than a singleton probably.
# jaybaz [MS] said on July 16, 2004 4:26 PM:
One way to avoid defining a new public type for each situation is to create a non-nested interface that you return in all different instances. Then the CWheelsHelper class can be private.

interface IIndexed<T, I> { T this[I index] { get; set;} }

...

public IIndexed<object, int> Wheels { get { return new WheelsHelper(this); } }

I still haven't provided a good answer about the singleton issue.
# Sean said on July 16, 2004 5:43 PM:

Mutability... good point.. now that you mention that I see how the posted generic response works better...
# RichB said on July 19, 2004 8:26 AM:
I just want to add more weight to Christoffer Skjoldborg's comment. I'm suprised more people aren't aware of how Indexers are implemented in MSIL.

Try writing Indexers with different names in C#, then use them from VB.Net - and vice versa. That's what I did when I was learning .Net.
# damien morton said on July 20, 2004 2:09 PM:
I was just implementing indexers, and I ended up using immutable structs as the actual indexers.

I think the foo.Wheels == foo.Wheels property is maintained in this case.

class Car
{
private object[] wheels;
public WheelsIndexer Wheels { get { return new WheelsIndexer(this); } }
public struct WheelsIndexer
{
readonly Car _outer;
public WheelsHelper(Car mc) { this._outer = mc; }
public object this[int index] { get { return this._outer.wheels[index]; } }
}
}
# jaybaz MS WebLog A way to do named indexers | Green Tea Fat Burner said on June 9, 2009 3:20 PM:

PingBack from http://greenteafatburner.info/story.php?id=1589

# jaybaz MS WebLog A way to do named indexers | adirondack chairs said on June 14, 2009 9:11 AM:

PingBack from http://adirondackchairshub.info/story.php?id=3186

New Comments to this post are disabled

This Blog

Syndication

Page view tracker