Indexed Properties in C# 4.0
- In C# 4.0 you can call parameterized properties declared in COM using the indexer syntax, for instance instead of excel.get_Range("a") you can now write excel.Range["a"].
- You can’t declare your own indexed properties from C#. We have no plans of adding the ability to declare your own properties with parameters. Instead, the recommended way is to use a type with an indexer.
- The feature is immediately available in Visual Studio 2010 Beta 2 for your indexing pleasure.
So I guess this hasn’t had a lot of press coverage so far (although Sam hinted about it). Even the published C# 4.0 language specification and the C# Future page still don’t mention it as of now. This is because we’ve implemented this feature very late in the cycle, as a DCR (Design Change Request). The language design team felt that we should complete the COM interop story in 4.0 and this one was the last missing piece of the puzzle. When Paul was in Redmond this summer, he was tasked with testing the IDE support for this feature, and I was helping out with the test infrastructure.
The new syntax
The pattern is very simple. Wherever you use COM interop and have to call get_X() and set_X(), now you can just call X, which we feel is a more natural syntax:
// before excel.get_Range("A1").set_Value(Type.Missing, "ID"); // after excel.Range["A1"].Value = "ID";
Let’s also take Scott Hanselman’s example from Beta 1:
var excel = new Excel.Application(); excel.Visible = true; excel.Workbooks.Add(); excel.get_ Range ( "A1" ) .Value2 = "Process Name"; excel.get_ Range ( "B1" ) .Value2 = "Memory Usage";
Now you can simplify this even further:
var excel = new Excel.Application(); excel.Visible = true; excel.Workbooks.Add(); excel.Range["A1"].Value = "Process Name"; excel.Range["B1"].Value = "Memory Usage";
This is just syntactic sugar – the compiler emits calls to the get_ and set_ accessors behind the stage.
In case that all the parameters are optional and none of the arguments are specified, you should omit the empty . Having Value is illegal.
This is the reason you can replace Value2 with Value in the example above: Value is an indexed property and you’re calling it without specifying any arguments – in this case we omit the brackets  altogether. Earlier, without indexed properties support, we had to introduce the ugly Value2, because you otherwise had to call get_Value().
My team, on the IDE side, provided IntelliSense support for this new language feature:
- completion list:
- parameter help:
- however Quick Info still shows you that in reality the call simply binds to the get_Range method:
As we were designing the feature, it turned out that adding compiler support for it is not the only tricky part. There were a couple of interesting problems in the IDE space as well. For example, what do you show in Quick Info for the following intexed property call?
Do we now show get_B or set_B?
When the IDE can’t guess which accessor we’re talking about (for example, in incomplete code), we by default bind to the get_accessor.
For backwards compatibility reasons, using the accessors get_ and set_ directly is still valid and available in IntelliSense, because we didn’t want to break all existing COM interop code out there.
Why not allow declaring indexed properties in C#?
A common question that we expect we’ll be getting is “why just consume? why not allow to declare such properties in C#?”. Well, the answer is not even that we first have to cost, design, spec, prototype, implement and test this feature, but rather that we think that declaring a type with an indexer is a preferred approach. It’s useful to separate the responsibilities:
- The property is there to get an object. The property belongs to the parent object.
- The indexer is there on the returned object to enumerate it. The indexer belongs to the returned object.
We shouldn’t be mixing these together.
Static or dynamic?
Accessing indexed properties is supported from both static and dynamic code.