Sunday, February 22, 2009

Now Is The Winter of Our Optional and Named Parameters

Optional and named method parameters are coming to C# 4. This means you can provide default values for method parameters. When you call the method, you can name only the parameters you want to specify - default values will be used for all other parameters.

Here's how you use it:

public void Foo (
    int doodad = 1,
    string humdinger = "",
    string wuchacallit = "STELLAAAA!")
{
    // Do stuff here
}

void Test ()
{
    Foo ();
    Foo (humdinger = "Beard Lust");
    Foo (doodad = 5,
         wuchacallit = "SHAMU!");
    Foo (wuchacallit = "Kia",
         humdinger = "Ora",
         doodad = 42);
}

Here's how it really works:

public void Foo (
    [Optional, DefaultParameterValue(1)]
    int doodad,
    [Optional, DefaultParameterValue("")]
    string humdinger,
    [Optional, DefaultParameterValue("STELLAAAA!")]
    string wuchacallit)
{
    // Do stuff here
}

void Test ()
{
    Foo (1, "", "STELLAAAA!");
    Foo (1, "Beard Lust", "STELLAAAA!");
    Foo (5, "", "SHAMU!");
    Foo (42, "Ora", "Kia");
}


The compiler just sprinkles the default values into every callsite. There are a few problems with this approach. Let's suppose I release version 1 of my awesome library with the above Foo method. You compile. All is well. Now let's say I release version 2 of my library, in which the default value "STELLAAAA!" is changed to "KHAAAAN!". But your code still has the old default value baked in. You need to re-compile your code to get the new default value. There is also the problem that injecting the full argument list into every callsite bloats the size of the code. Bigger code means more to JIT and fewer cache hits

How it should work:

struct FooSettings {
    int doodad_value = 1;
    string humdinger_value = "";
    string wuchacallit_value = "STELLAAAA!";

    public int doodad {
        get { return doodad_value; }
        set { doodad_value = value; }
    }
    public string humdinger {
        get { return humdinger_value; }
        set { humdinger_value = value; }
    }
    public string wuchacallit {
        get { return wuchacallit_value; }
        set { wuchacallit_value = value; }
    }
}

public void Foo (FooSettings settings)
{
    // Do stuff
}

void Test ()
{
    Foo (new FooSettings ());
    Foo (new FooSettings { humdinger = "Beard Lust" });
    Foo (new FooSettings {
        doodad = 5,
        wuchacallit = "SHAMU!" });
    Foo (new FooSettings {
        wuchacallit = "Kia",
        humdinger = "Ora",
        doodad = 42 });
}


Thanks to C# 3's object initialization, you can squint at the call and almost see the named parameter syntax (just ignore "new FooSettings"). This pattern of using a special "settings" type for passing arguments to methods already exists in the framework (see XmlReader.Create and XmlWriter.Create for an example). I am proposing that the compiler auto-generate these types and provide full optional/named parameter sugar. The compiler-generated types would be publicly nested within the type containing the method and named "[MemberName]Settings" by default.

This is better than callsite default value injection because:
  • It versions well

  • It adds a fixed amount of additional code (the type), whereas injection adds more code every time you use it

  • It is CLS compliant

This is how C# 4 should do optional and named parameters.

Saturday, February 14, 2009

Generic Type Parameters AS Method Parameters

I have long had an interest in method contracts (pre- and post-conditions). I followed Spec# and I continue to follow the Pex project. I am also a big fan of doing argument verification at the highest possible level of a public API. I was working on a public API today which takes a Type object. My method looked like this:

public string GetClassName (Type type) {
    if (type == null) {
        throw new ArugmentNullException ("type");
    }

    if (!type.IsSubclassOf (typeof (UpnpObject)) &&
        type != typeof (UpnpObject)) {
        throw new ArgumentException (
            "The type does not derive from UpnpObject.",
            "type");
    }

    // do stuff with 'type'
}


It then occurred to me that I can do the same thing with a generic type parameter, but get all the checks for free!

public string GetClassName<T> () where T : UpnpObject {
    //do stuff with 'typeof (T)'
}


Tada! Using generic type parameters as method parameters is nothing new (Aaron has something like this in the Banshee service stack), but the really neat thing is that you can use the generic constraints as a kind of argument pre-condition. If you have a method which takes a Type, consider using a generic type parameter rather than a method parameter. It guarantees that 'null' cannot be passed and it allows you to specify ancestry, interfaces, ref/value types, and the presence of a default constructor.

Tuesday, February 3, 2009

C# 4 is NOW!

WHAT?
Generic type variance support just landed in mcs. This is a C# 4 language feature.

WHERE?
You can give it a go by checking out SVN trunk and compiling your variant code with gmcs -langversion:future.

REALLY?
Well, this adds compiler support for variance but the Mono VM isn't up to speed on its variance handling. This means that you can compile the code but it won't actually run on Mono (until we fix that, which I am also doing). You can run it on the .NET 2.0 VM.

WHAT THE HELL ARE YOU TALKING ABOUT?
Generic type variance is like this:

Let's say I have some IEnumerable<string>, like so:

IEnumerable<string> myStrings = GetSomeStrings ();


Now let's say I have some other method which takes an IEnumerable<object>, like so:

void DoStuff (IEnumerable<object> someObjects)
{
    foreach (object o in someObjects) {
        // do some stuff with each object
    }
}


POP QUIZ: Can I pass myStrings to DoStuff in C# 3? Strings are objects, right? And IEnumerable<T> is just a way of getting Ts. So if strings are objects, and IEnumerable<string> just returns strings, then we can also say that it returns objects. Just like IEnumerable<object>. So it should work, right?

ANSWER: Negatorz!

This is a problem of generic type variance. There are two kinds of variance: covariance and contravariance. The above example is covarant, meaning that you want to broaden the type of an output. Contravariance is the opposite: narrowing the type of an input. Let's consider a delegate:

delegate void Handler<T> (T input);


And let's say that we have some Handler<object>:

Handler<object> myHandler = delegate (object o) {
    // do something with the object
}


Now let's say that we have a method with takes a Handler<string>

void HandleStrings (Handler<string> handler, IEnumerable<string> strings)
{
    foreach (string s in strings) {
        handler (s);
    }
}


We want to pass myHandler to HandleStrings. A Handler<object> takes objects, and strings are objects, so anything which is a Handler<object> should also be a legal Handler<string>. This is an example of contravariance.

It may surprise you to know, but the CLI has supported generic type variance since version 2. The rules are:
  • Variant type parameters are only allowed in interfaces and delegate types.

  • Contravariant type parameters can only be used as by-value method parameter types.

  • Covariant type parameters can only be used as method return types and generic arguments to inherited interfaces.

  • Only reference types are variant (this isn't explicitly stated in the spec, but it is the case).

  • Languages may choose to ignore variance and treat all generic parameters as invariant.


For whatever reason, the C# language team has so far chosen not to support generic type variance. Well, that will be changing in 2010. The preview given by Anders Hejlsberg at PDC '08 revealed that C# 4 will finally support variance. But who wants to wait? Especially considering that this has been a .NET VM feature since 2006. So you can now use this in gmcs if you pass -langversion:future.

Covariance (which, as you will remember, can only be used as a method return type or as a generic argument to an inherited interface) is denoted with the "out" keyword before the type parameter identifier:

interface IFoo<out T> : IBar<T>
{
    T Bat { get; }
}


Contravariance (which is legal only as the type of a by-value method parameter) is denoted with the "in" keyword:

interface IFoo<in T>
{
    void Bar (T bat);
}


So there you go! Now we just need to get Mono's VM variance support polished off. I'm sure I'll have good news for you about that shortly. Thanks goes to Marek Safar for reviewing patches. If you have questions about how or why variance works (it's kind of tricky to get your head around), leave a comment. I might do a post delving into all of the little rules behind variance.