views:

457

answers:

5

I know, I know... Eric Lippert's answer to this kind of question is usually something like "because it wasn't worth the cost of designing, implementing, testing and documenting it".

But still, I'd like a better explanation... I was reading this blog post about new C# 4 features, and in the section about COM Interop, the following part caught my attention :

By the way, this code uses one more new feature: indexed properties (take a closer look at those square brackets after Range.) But this feature is available only for COM interop; you cannot create your own indexed properties in C# 4.0.

OK, but why ? I already knew and regretted that it wasn't possible to create indexed properties in C#, but this sentence made me think again about it. I can see several good reasons to implement it :

  • the CLR supports it (for instance, PropertyInfo.GetValue has an index parameter), so it's a pity we can't take advantage of it in C#
  • it is supported for COM interop, as shown in the article (using dynamic dispatch)
  • it is implemented in VB.NET
  • it is already possible to create indexers, i.e. to apply an index to the object itself, so it would probably be no big deal to extend the idea to properties, keeping the same syntax and just replacing this with a property name

It would allow to write that kind of things :

public class Foo
{
    private string[] _values = new string[3];
    public string Values[int index]
    {
        get { return _values[index]; }
        set { _values[index] = value; }
    }
}

Currently the only workaround that I know is to create an inner class (ValuesCollection for instance) that implements an indexer, and change the Values property so that it returns an instance of that inner class.

This is very easy to do, but annoying... So perhaps the compiler could do it for us ! An option would be to generate an inner class that implements the indexer, and expose it through a public generic interface :

// interface defined in the namespace System
public interface IIndexer<TIndex, TValue>
{
    TValue this[TIndex index]  { get; set; }
}

public class Foo
{
    private string[] _values = new string[3];

    private class <>c__DisplayClass1 : IIndexer<int, string>
    {
        private Foo _foo;
        public <>c__DisplayClass1(Foo foo)
        {
            _foo = foo;
        }

        public string this[int index]
        {
            get { return _foo._values[index]; }
            set { _foo._values[index] = value; }
        }
    }

    private IIndexer<int, string> <>f__valuesIndexer;
    public IIndexer<int, string> Values
    {
        get
        {
            if (<>f__valuesIndexer == null)
                <>f__valuesIndexer = new <>c__DisplayClass1(this);
            return <>f__valuesIndexer;
        }
    }
}

But of course, in that case the property would actually return a IIndexer<int, string>, and wouldn't really be an indexed property... It would be better to generate a real CLR indexed property.

What do you think ? Would you like to see this feature in C# ? If not, why ?

+6  A: 

Because you can already do it kind of, and it's forced you to think in OO aspects, adding indexed properties would just add more noise to the language. And just another way to do another thing.

class Foo
{
    public Values Values { ... }
}

class Values
{
    public string this[int index] { ... }    
}

foo.Values[0]

I personally would prefer to see only a single way of doing something, rather than 10 ways. But of course this is a subjective opinion.

Ion Todirel
+1, this is a much better way of implementing it than gumming up the language with VB5 constructs.
Josh Einstein
+1 because this is how I would do it. And if you were good you could probably make this generic.
Tony
+1  A: 

Well I would say that they haven't added it because it wasn't worth the cost of designing, implementing, testing and documenting it.

Joking aside, its probably because the workarounds are simple and the feature never makes the time versus benefit cut. I wouldn't be surprised to see this appear as a change down the line though.

You also forgot to mention that an easier workaround is just make a regular method:

public void SetFoo(int index, Foo toSet) {...}
public Foo GetFoo(int index) {...}
Ron Warholic
Yes, but IMHO a method doesn't really convey the same meaning as a property...
Thomas Levesque
Very true. If the property syntax is absolutely vital then you can use Ion's workaround (perhaps with some generics to allow for a variety of return types). Regardless, I think this speaks to the relative ease of getting the same job accomplished without additional language features.
Ron Warholic
+5  A: 

A C# indexer is an indexed property. It is named Item by default (and you can refer to it as such from e.g. VB), and you can change it with IndexerNameAttribute if you want.

I'm not sure why, specifically, it was designed that way, but it does seem to be an intentional limitation. It is, however, consistent with Framework Design Guidelines, which do recommend the approach of a non-indexed property returning an indexable object for member collections. I.e. "being indexable" is a trait of a type; if it's indexable in more than one way, then it really should be split into several types.

Pavel Minaev
+19  A: 

Here's how we designed C# 4.

First we made a list of every possible feature we could think of adding to the language.

Then we bucketed the features into "this is bad, we must never do it", "this is awesome, we have to do it", and "this is good but let's not do it this time".

Then we looked at how much budget we had to design, implement, test, document, ship and maintain the "gotta have" features and discovered that we were 100% over budget.

So we moved a bunch of stuff from the "gotta have" bucket to the "nice to have" bucket.

Indexed properties were never anywhere near the top of the "gotta have" list. They are very low on the "nice" list and flirting with the "bad idea" list.

Every minute we spend designing, implementing, testing, documenting or maintaining nice feature X is a minute we can't spend on awesome features A, B, C, D, E, F and G. We have to ruthlessly prioritize so that we only do the best possible features. Indexed properties would be nice, but nice isn't anywhere even close to good enough to actually get implemented.

Eric Lippert
Can I add a vote for putting it on the bad list? I don't really see how the current implementation is much of a limitation when you could just expose a nested type that implements an indexer. I would imagine you'd start seeing a lot of hackery to try to shoehorn something into databinding and properties that should be methods.
Josh Einstein
And hopefully auto-implemented INotifyPropertyChanged is way higher on the list than indexed properties. :)
Josh Einstein
@Eric, OK, that's what I suspected... thanks for answering anyway ! I guess I can live without indexed properties, as I've done for years ;)
Thomas Levesque
@Josh, see this discussion on UserVoice : http://dotnet.uservoice.com/forums/40583-wpf-feature-suggestions/suggestions/478802-modify-the-language-to-allow-for-observable-proper?ref=title
Thomas Levesque
I hope that "static indexers" (like Encoding["UTF8"]) will at some point make it to the good list :)
Michael Stum
Eric, why doesn't C# get more people and more budget? Scala progresses really fast..
Martin Konicek
@Martin: I am not an expert on how large software teams are budgets are determined. Your question should be addressed to Soma, Jason Zander or Scott Wiltamuth, I believe all of whom write blogs occasionally. Your comparison to Scala is an apples-to-oranges comparison; Scala does not have most of the costs that C# does; just to name one, it does not have millions of users with extremely important backwards compatibility requirements. I could name you many more factors that could cause massive cost differences between C# and Scala.
Eric Lippert
@Martin: And besides, suppose magically our budget increased by 50%. That doesn't change the fact that we still have *hundreds* of times more potential features than budget to implement them. It doesn't change the fact that we still have to prioritize. And it negatively impacts our largest cost of all: the cost of growing the language too big such that it becomes difficult or impossible to design new features that interact smoothly with existing features. More budget to add more features makes that problem *worse*, not *better*.
Eric Lippert
@Eric Lippert: Thank you for explanation. You are right about the Scala comparison. The budget could help with the "must have" features that are being postponed now. I understand it is a question for someone else. Thank you
Martin Konicek
+1  A: 

Another workaround is listed at http://stackoverflow.com/questions/3344620/easy-creation-of-properties-that-support-indexing-in-c, that requires less work.

EDIT: I should also add that in response to the original question, that my if we can accomplish the desired syntax, with library support, then I think there needs to be a very strong case to add it directly to the language, in order to minimize language bloat.

cdiggins
Thanks. Actually, I had implemented exactly the same thing some time ago...
Thomas Levesque
In answer to your edit: I don't think it would cause language bloat; the syntax is already there for class indexers (`this[]`), they just need to allow an identifier instead of `this`. But I doubt it will ever be included in the language, for the reasons explained by Eric in his answer
Thomas Levesque
Good point Thomas.
cdiggins