Saturday, September 09, 2006 11:13 AM bart

Some notes on the IndexerNameAttribute in C#

When creating a little sample of some C# features, I created my much beloved vector class with operator overloading and indexer support. No rocket science whatsoever but when concentrating on demonstrating the cross-language features of the CLR, I quickly realized again that indexers are not directly available in some other .NET languages like J#.

Assume the following C# definition of a Vector class (I "trivialized" the class to keep the further elaboration to the point):

class Vector
{
   public string this[int i]
   {
      get { return String.Empty; }
      set { }
   }
}

Behind the scenes this creates a property with (as usual) two methods, a getter and a setter:

.property instance string Item(int32)
{
.get instance string VectorDemo.Vector::get_Item(int32)
.set instance void VectorDemo.Vector::set_Item(int32,string)
} // end of property Vector::Item

However, watch the property's name: Item. This is the default name for indexers.

When you keep yourself busy with C# programming, this is not much of a problem as you won't ever see this property directly in IntelliSense. The usage of this class is of course like this:

Vector v = new Vector();
string s = v[0];

However, when creating libraries you should care about other languages too, where indexers are not directly supported and should be called through the corresponding property. In J# our vector class behaves as follows:

Not too bad, but in some cases you might want to give the indexer another name. That's why the CLR and compiler gods invented IndexerNameAttribute. It works like this:

class Vector
{
   [
IndexerName("Component"
)]
   public string this[int
i]
   {
      get { return String.Empty; }
      set { }
   }
}

In maths, vectors consist of components so the name Component might be well-chosen. When you don't have good reasons to change the default indexer name, you should stick with the default Item name (D.2.1.3 of the Class Library Design Guidelines of Partition V of the ECMA-335 spec: "Do use the name Item for indexed properties unless there is an obviously better name."). I agree you can discuss about this in the context of a vector but nevertheless it's good to know about the IndexerNameAttribute in case you need it.

How it works is pretty simple. First it generates the new property, now called Component instead of the default Item name:

The question now is how the C# compiler (and VB compiler and every other .NET language compiler that supports indexers as a consumer) knows the name of the corresponding property to emit:

Vector v = new Vector();
string s = v[0];

becomes

IL_0008: ldc.i4.0
IL_0009: callvirt instance string VectorDemo.Vector::get_Component(int32)

The answer is metadata; the Vector class gets decorated with a DefaultMemberAttribute like this:

.class public auto ansi beforefieldinit VectorDemo.Vector
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Reflection.DefaultMemberAttribute::.ctor(string) = ( 01 00 09 43 6F 6D 70 6F 6E 65 6E 74 00 00 ) // ...Component..
} // end of class VectorDemo.Vector

Simple isn't it? However, make sure to recompile every consumer of the library too because the indexer's property was renamed (example for a VB consumer that wasn't recompiled but only got the new version of the vector library):

C:\Users\Bart\Documents\Visual Studio 2005\Projects\VectorDemo\IndexerConsumer\bin\Debug>indexerconsumer.exe

Unhandled Exception: System.MissingMethodException: Method not found: 'System.String VectorDemo.Vector.get_Index(Int32)'.
   at IndexerConsumer.Module1.Main()

Cheers!

Del.icio.us | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:

Comments

No Comments