Share via


Irony, thy name is C#

Intro:

So i was having a conversation with the team about a small change I'd like to make to C#. Right now, C# has the concept of "contextual keywords". I.e. identifiers that can have special meaning when used in special locations, but which are otherwise completely legal identifiers for the user in all other conditions. Good examples of this are 'partial', 'yield', and 'where'. In C# 2.0 "yield" is now used in iterator contexts to indicate that you are yielding a value, or terminating iteration. However, in C# 1.0 "yield" was a perfectly valid identifier. In fact, I used it all the time because i had a type called "class Yield" which indicated the 'yield' of a gramamtical production in a language. So how could we add this "contextual keyword" without breaking code that had been previously written? Simple, we added it in a way such that if you tried to use it with its new meaning you would be writing code that couldn't exist in previous versions of the language. Specifically, to use "yield" you must use it like so:

yield return 4;

yield break;

By forcing you do use "yield" before a "return" or "break" keyword we ensure that we won't be conflicting with any code that you've written in the past (where this would have been illegal).

So this is great. We can add more functionality to the C# language without impacting you negatively.

Now, i was doing some C# coding and i was trying to use a common java idiom that i find very natural. Specifically:

InputStream in = ...;

OutputStream out = ...;

But, lo and behold, i'm not allowed to! Turns out 'in' and 'out' are reserved keywords and i can't use them as variable names. So i was giving it a little thought and i realized: "hey these don't have to be keywords, we could make them contextual keywords!". Why is that the case? Well, let's look at how 'out' is used. 'out' can only be used in three places, the declaration of a method or delegate and the call site of to a method, a-la:

void Foo(out int i) { //method declaration

      int num;

      Foo(out num); //method call

However, as a user, you are not allowed to write code with arbitrary identifiers preciding an argument to a method, or preceding the type in a method declaration. So we could make 'out' contextual, without screwing up any of your code, and while now allowing it as an identifier name. What would be interesting is that we would now allow this type of code:

void Foo(out out out) { //method declaration

 

But no one will do that (or else they'll be beaten in the code review). Regardless, such cases exist today with things like "where". You could say:

void Foo<where>() where where : where

 

and we won't stop you. Just in practice this will never happen. (and, if it did, the IDE colorization would help you out a lot).

A similar story holds for "in", which is reserved solely for the "foreach" construct. I won't delve deeper into this since i think you get the idea.

 

Story:

So we're having a conversation about things we'd like to see in the next version of the language. I bring this up, mention the benefits and how it would just be a nice thing to do with very low cost. There is agreement, but a lot of wondering "is the benefit so small that we shouldn't even be looking at this". Ok, so we talk about it for like 2 minutes and then move on. At some point we get to another suggestion of mine whereby i think that we should allow a delegate to bind to a property. i.e. you could have the following:

delegate int ProducesInt();

delegate void TakesInt(int i);

class Person {

      int Age { get { ... } set { ... } }

      void Foo() {

            ProducesInt pi = new ProducesInt(this.Age);

            TakesInt ti = new TakesInt(this.Age);

Of course, this isn't allowed today because the argument to a delegate must be a method. But this always ends up getting in my way and i have to create nasty workarounds. Since we're the compiler we know what the delegate binds to, and we know about the synthesized methods created for the getter and setter, and so it would be pretty trivial to pass the right method object to the delegate's constructor. In the cases above we would just traslate that to:

           

ProducesIntpi =newProducesInt(this.get_Age);

TakesIntti =newTakesInt(this.set_Age);

So, we got to talking about it a bit more, and we came back to the old discussion where we get a lot of requests from customers for an easy way to refer to a property's getter or setter easily in the language. Me, thinking i was so brilliant, said: "how about the following idea:

           

ProducesIntpi=newProducesInt(this.Age.get);

TakesIntti =newTakesInt(this.Age.set);

By using the 'get' or 'set' keyword after referring to a property, we will then get the appropriate method off of it that you can now use. Handy for this situation and others as well!"

And, instantly, someone responded: "because 'get' and 'set' are contextual keywords, and it's perfectly valid for them to be members of a type. You now have an ambiguous situation where you might be referring to the getter of the property, or a field on the object that is returned."

Bam. Right after I'd argued passionately about moving more keywords to be contextual.

I hadn't considered that reserving keywords could actually be useful because they did allow us a lot more flexibility with creating new language features. If we'd reserved 'get' and 'set', then this would be trivially easy to solve, but now we're stuck in a place where doing that is no longer as elegant or easy, and we don't want to bog down the language with more uglyness.

So i backed out of my request for 'in' and 'out' to become contextual. After all, it's not really hurting people that badly, and it does give us more flexibility in the future if we find that those keywords would be perfect for something else. It also meant that i could focus more on getting features that i really want into the language (like covariant return types. have you voted yet???).

Comments

  • Anonymous
    March 20, 2005
    There is always code like http://blogs.msdn.com/michkap/archive/2005/02/07/368570.aspx where every single identifier can look like the exact same term -- whether namespace, class, variable, or procedure. :-)

  • Anonymous
    March 20, 2005
    It would be nice to be able to make delegates accept properties, but only in the few cases where I want it to work like that. It is a nice to have feature, not a must have.

  • Anonymous
    March 20, 2005
    Good to know that at least one person in Microsoft also thinks that delegates to properties should be allowed. I'd be happy with using "get_Age" and "set_Age" but the C# compiler seems to have some artifical ban on referring to those methods directly in any context.

    I don't know why this isn't supported, as it's a perfect type-safe and fast method of data binding. No reflection would be required.

    I'd take it a bit farther, and have a PropertyBinder<T> with special compiler support such that doing this:

    PropertyBinder<int> ageBinder = person.Age; //creates an instance by calling PropertyBinder<int>.ctor(person.get_Age, person.set_Age)
    int age = ageBinder.Value;
    ageBinder.Value;

    Perhaps take it a step further and have a binder that could bind to multiple objects (since delegates can do this) and enumerate all the bound values, see if all the bound values are equal, set all bound values at once, etc. The "+" operator can be overloaded in the same way it is for delegates.

  • Anonymous
    March 20, 2005
    The comment has been removed

  • Anonymous
    March 20, 2005
    This is quite amusing. I just wrote about this same issue in VBScript.

    http://blogs.msdn.com/ericlippert/archive/2005/03/16/396903.aspx

    In VBScript,

    For [i=1]=.For To Step Step Step

    is perfectly legal, for similar reasons.

  • Anonymous
    March 20, 2005
    So how about this:

    ProducesInt pi = new ProducesInt(this.Age.out);
    TakesInt ti = new TakesInt(this.Age.in);

  • Anonymous
    March 20, 2005
    Robert: THat's fantastic. I love it.

    highly doubt it would happen. since in/out get/set would just confuse people,

  • Anonymous
    March 20, 2005
    Blog link of the week 11

  • Anonymous
    March 20, 2005
    I think the syntax you posted at the beginning was fine.

    ProducesInt pi = new ProducesInt(this.Age);
    TakesInt ti = new TakesInt(this.Age);

    .net just chooses the appriate field method based on the delegate signature.

    What is wrong with that?

  • Anonymous
    March 20, 2005
    James: I'm sorry if my point wasn't clear. Yes, my original suggestion still works. The point was to see if we could go with a cleaner approach which would work in all situations. The intuitive solution was shot down for the exact things i was earlier advocating. That was what was funny.

  • Anonymous
    March 20, 2005
    Why not make using get_Age and set_Age a warning rather than an error in this case? At least then we could put in a #pragma and be on our happy way.

    This is just yet ANOTHER reason why I'd like inline IL in C#.

  • Anonymous
    March 20, 2005
    You can use @in and @out as variable names if you need them badly.

  • Anonymous
    March 20, 2005
    This is not exactly what you want, but would be a time saver for the intellisense spoiled people like myself..

    Scenario 1:

    InputStreamTAB

    Normally this would produce:
    InputStream<either TAB or spaces>

    But with TAB completion:

    InputStream inputStream

    pressing TAB again:
    InputStream inputStreamTAB

    gives, if InputStream can be instantiated:
    InputStream inputStream = new InputStream(

    Otherwise you could get a list of derived types or whatever..

    WHATS WITH THE "
    " prefix ???

    Well that "
    " will act as a signal to the developer that this variable name was inferred from the class name and is candidate for renaming!


    So if you have a second to answer back in the comments, what you think of this or does VS already have this kind of variable name "generator" ? I know the Paste Parameter Tip but the completion etc stuff could be taken a bit further..

  • Anonymous
    March 20, 2005
    To make it context keywords why not :

    ProducesIntpi=newProducesInt(get this.Age);
    TakesIntti =newTakesInt(get this.Age);

  • Anonymous
    March 20, 2005
    mmm, delegates on properties would be nice to have. Not my number one priority by a long shot, but definately handy to have.

  • Anonymous
    March 21, 2005
    Andrew, I currently do like that. 2-3 letter abreviations of the classnames. However after a while there can be many and "pi" could both refer to ProducesInt as well as ProcessInvoker etc.. So better would be autogenerated processInvokerA processInvokerB etc..

  • Anonymous
    March 22, 2005
    I would like delegates to overloaded methods. I don't care if a delegate is implemented as a type and that's why it's not possible. A delegate looks like a method to me, hence it should obey the same rules.

    And please don't add more keywords to C#. You are killing the language.

  • Anonymous
    March 23, 2005
    Thomas: where did you get the idea that we were adding new keywords to the language? No new keywords have been added since c# 1.0

    Also, as you can see, my preface was about removing keywords from the language so that you could use them.

  • Anonymous
    March 24, 2005
    yield is not a new keyword?

  • Anonymous
    March 24, 2005
    Thomas: No, it isn't. As i explained, it's a "contextual keyword". i.e.

    "Right now, C# has the concept of "contextual keywords". I.e. identifiers that can have special meaning when used in special locations, but which are otherwise completely legal identifiers for the user in all other conditions."

  • Anonymous
    March 25, 2005
    Calling it keyword or contextual keyword is splitting hairs. I see how they are different, but when you use them as keywords, they are keywords. And I think C# has too many of them already.

  • Anonymous
    January 20, 2008
    PingBack from http://websitescripts.247blogging.info/cyrus-blather-irony-thy-name-is-c/

  • Anonymous
    June 08, 2009
    PingBack from http://cellulitecreamsite.info/story.php?id=5454