Ayende @ Rahien

Refunds available at head office

Why C# doesn't have extension properties

I just run into this problem, and I came up with a different reason than the usual one. C# simply doesn't have the concept of indexed properties. This is not legal C# code:

public static string Items[string something]
{
    get { return something; }
}

I think you can do that with VB.Net, and I am certain that C++ supports it.

The interesting part is that I run into it while building a DSL. The limit of the implementation language has actually limited the DSL itself.

Comments

Niki
12/02/2007 10:25 PM by
Niki

Why don't you just create an ordinary property that returns an object that implements an indexer property? I always thought that was the "accepted C# workaround" for that situation.

Ayende Rahien
12/02/2007 10:35 PM by
Ayende Rahien

Take a look here:

https://rhino-tools.svn.sourceforge.net/svnroot/rhino-tools/trunk/rhino-commons/Rhino.Commons/Indexers.cs

And I wanted to add a extension property to a Boo DSL, but I couldn't, since C# doesn't support this.

Simeon Pilgrim
12/02/2007 10:37 PM by
Simeon Pilgrim

You are correct, VB.Net does support named indexers, in fact all indexers are named, and you mark one as the default. The documentation states you should only have a single default to be compatible with other languages (C#)

Here si the MSDN on how todo it in C++ or VB.Net

http://msdn2.microsoft.com/en-us/library/52d21xwx.aspx

pat@veloc-no-spam-porfavor-it.com (Pat Gannon)
12/02/2007 11:48 PM by
pat@veloc-no-spam-porfavor-it.com (Pat Gannon)

Do you mean to say that C# doesn't support static indexed properties? One can of course do an indexed property at the 'instance level' by doing the following:

public string this[string something]
{
get { return string.Empty; }
}

Ayende Rahien
12/02/2007 11:51 PM by
Ayende Rahien

Pat, no, you can't do an indexed property, only an indexer, which is another thing entirely.

Alex Henderson
12/03/2007 12:18 AM by
Alex Henderson

Yeah, I've always found it puzzling that they didn't let you specify indexed properties in C# ... surely by convention they could've just let you do it, with indexers mapping to some reserved indexer name.

I hadn't considered how this effects implementing extension properties... it could well be one of the flys in the ointment preventing it from being implemented in a language-neutral fashion.

Tobias Fjälling
12/03/2007 07:55 AM by
Tobias Fjälling

My guess is that the C# team did not allow it strictly from a usability aspect since properties would look too much like methods. Which they are not from a syntax point of view in the code (but are at an IL level). So when an input parameter is needed, a method should be used instead.

Nevertheless, I certainly miss them. There have been several occasions when I would have liked to add a parameter or two for a property setter. I always end up with "oh yeah, that's right. Not possible here..."

Sometimes I find it hard to decide whether to use a read-only property or a method and I kind of have made up my own convention in the area of when to use which. But does anyone have some good source of convention recommendations here?

Felipe Garcia
12/03/2007 12:39 PM by
Felipe Garcia

Once I needed this kind of functionality, so I ended up with something like this chunk of code - it's not pratical, neither elegant, but did the trick.

public class DictionaryIndexerDecorator<K, V>

{

    private IDictionary<K, V> _items;

    public DictionaryIndexerDecorator(IDictionary<K, V> items)

    {

        _items = items;

    }


    public V this[K key]

    {

        get

        {

            return _items[key];

        }

    }

}


public class Bag

{

    private IDictionary<string, object> _items;

    private DictionaryIndexerDecorator<string, object> _indexer;


    public Bag()

    {

        _items = new Dictionary<string, object>();

        _indexer = new DictionaryIndexerDecorator<string, object>(_items);

    }


    public void Add(string key, object item)

    {

        _items.Add(key, item);

    }


    public DictionaryIndexerDecorator<string, object> Items

    {

        get

        {

            return _indexer;

        }

    }

}
Luke Breuer
12/03/2007 02:42 PM by
Luke Breuer

Surely you could quickly (5 minutes) build an IDictionary-implementing type that allows you to pass in delegates for the Get and Set, and then expose that dictionary as a named property? You could use lambdas, anonymous delegates, or type-level methods for the Get and Set. The end result, for the user of your code, is identical to VB.NET named indexed methods, as far as I can tell (unless multiple arguments are allowed). This syntax sugar should be easily applicable to Boo code and translated transparently. Or am I missing something?

N.B. My option provides Get (Set is really optional), which allows more flexibility than Felipe's solution above.

Ayende Rahien
12/03/2007 02:45 PM by
Ayende Rahien

Luke, yes, you can.

Except that for extension properties, you need a property, not a fake one.

Luke Breuer
12/03/2007 02:47 PM by
Luke Breuer

Ehhh, you're right, the words "extension" and "static" caused an InvalidOperationException that I handled by swallowing. Bad me! You'd have to do SomeMethod()[indexed value] to get your desired syntax, which would be kind of gross. However, Boo could still add the syntactic sugar for this, could it not?

Ayende Rahien
12/03/2007 02:51 PM by
Ayende Rahien

Luke,

Probably, but since the Boo implementation of this is simply:

[Extension]

static Foo[indexer as string]:

get:

   return indexer

"bar".Foo

I don't really want to get to that

Luke Breuer
12/03/2007 02:57 PM by
Luke Breuer

"I don't really want to get to that" -- parse error. Do you want ideal C# syntax, or decent C# syntax and ideal Boo syntax?

Ohh, I forgot something in my recent email to you -- why should I have to type in the four-letter code when I've made plenty of comments that weren't spam, all from the same IP that has never submitted spam? :grrrr:

Ayende Rahien
12/03/2007 03:05 PM by
Ayende Rahien

It means that I can do that, but I don't want to get to this level

Ayende Rahien
12/03/2007 03:05 PM by
Ayende Rahien

It means that I can do that, but I don't want to get to this level

Luke Breuer
12/03/2007 03:09 PM by
Luke Breuer

You could be a bit more verbose -- "this level"? Speaking of which, I did give you a solution for static, named indexers -- just not static, named, extension indexers. The InvalidOperationException of which I spoke was in part due to your use of "extension" in the blog post title but not the blog post text.

Ayende Rahien
12/03/2007 03:11 PM by
Ayende Rahien

This level - low level messing around with the compiler, to get something this simple to work.

I agree that you can have named indexers, but that doesn't solve the problem of extension properties, which is how I got to this.

Verbosity, I am at work, and I am trying to keep an eye on mail & work at the same time.

Luke Breuer
12/03/2007 03:14 PM by
Luke Breuer

I do not recall seeing any good, juicy discussions of when it is proper to use extension XXX (Methods are all that are allowed now, but Properties and other things are theoretical possibilities)? When I say "juicy", I mean discussion about core principles, such as cohesion, loose coupling, etc. Readability (or, as Scott Bellware put it, solubility). Have you seen/had any such discussions?

Ayende Rahien
12/03/2007 03:19 PM by
Ayende Rahien

There are several threads about that in the alt.net mailing list.

Thomas Krause
12/03/2007 04:35 PM by
Thomas Krause

IIRC an indexer in C# will simply create an indexed Item property.

To work as an indexer the class is then decorated with the DefaultMemberAttribute.

You can change the name of the property it creates with the IndexerNameAttribute. C# will hide this property from you, but maybe boo does not...

Of course you would still have the problem that this property will also act as an indexer/default property and it propable doesn't work with static members. Also you can probably only create one indexed property per class with this method.

So it may not be useful at all to you, just thought it might be interesting to know (then again you propably do know this already)... ;-)

Jeremy Gray
12/03/2007 04:39 PM by
Jeremy Gray

As far as I'm concerned, while properties are useful syntactical sugar for certain method calls, once you are off into having parameterized properties you should be writing them and calling them as what they are: methods.

I'm currently working with a codebase where someone got the bright idea that parameterized properties in their VB.net code and let me tell you that the result, a few years later, is a bloody maintenance annoyance, especially when used in VB where the damn things get called using the exact same syntax as methods, and where when called from C# you have to use the explicit get_ syntax. Just don't do it.

If you really, really, really want to cook up syntax of this kind: Expose a property that is of a type that has a variety of indexers on it. I've done this when building some in-C# DSL-ish fluent APIs, which is probably the one place that I would be willing to bend the MSFT guidelines around indexer behaviour, and it works brilliantly.

Ayende Rahien
12/03/2007 05:03 PM by
Ayende Rahien

Jeremy,

DSL building is the scenario that is under discussion

Jeremy Gray
12/03/2007 05:28 PM by
Jeremy Gray

Yes, Oren, it is. Did you somehow get the impression that I didn't realize that?

We both know full well that when writing DSLs (or even high quality fluent interfaces) you as the developer of said DSL will have to go well out of your way in order to craft a result that is easily usable. In this case, I'd suggest dropping the argument against the lack of parameterized properties in C# and focus on providing a solution that integrates well with all of the available languages. I've already told you how you can do that, and so have others.

Oren, no offense here, but I do need to take a moment to say something: I love reading your blog and plan to keep coming back, but you could do with doing a bit less moaning and whining these days. Please get back to showing us all awesome ways to achieve things.

Comments have been closed on this topic.