Covariance and void return types

Reading through chapter 2 (see, I’m being good) a new thought about return type covariance occurred to me. This is odd in itself, because I thought I’d exhausted my own supply of ideas around variance (which isn’t the same as knowing everything about it, of course – it just means I don’t expect to have anything new to say).

Just as a reminder, in C# 1 delegates were completely invariant – in order to create a delegate instance from a method group, for instance, the signature had to match exactly. For instance, suppose we had a delegate type declared as:

delegate object ObjectFactory();

We couldn’t (in C# 1) use the following method to create an instance of ObjectFactory:

public StringBuilder CreateStringBuilder()
{
    return new StringBuilder();
}

Even though a StringBuilder is always an object, the signatures don’t match exactly, and it was prohibited.

C# 2 allows this, however, so it’s legal to write:

ObjectFactory factory = CreateStringBuilder;

So far, so good. (Yes, a generic delegate type would be nicer, but I’m avoiding the variance issues with generics for this post. Let’s not make things more complicated than they need to be.) Just to be absolutely clear about this, it’s valid and legal because there’s nothing you can do with factory now which will break normal type safety. You can call factory() and be absolutely sure that it will return an object of some kind (or null) so it’s as safe as anything else.

Now, what about a delegate type which has a void return type:

delegate void Action();

You can’t use CreateStringBuilder as the target of an instance of Action – C# 2 and even C# 3 completely disallow it. My guess is that the CLR disallows it internally. Why is this? Again, any use of an Action delegate can’t possibly care about what the target returns, because it’s not declared to return anything.

I strongly suspect that the answer lies in the implementation of the CLR rather than in any deep semantic reason – the CLR probably needs to know whether or not there’s going to be a return value, in order to do appropriate things with the stack. Even so, it seems a bit of a pity, in terms of elegance. I can’t say I’ve ever felt the need for this in real life, and it would be reasonably easy to fake (for up to four parameters) in .NET 3.5 just by writing a converter from Func<X> to Action<X>, Func<X,Y> to Action<X,Y> etc. It niggles a bit though :)

2 thoughts on “Covariance and void return types”

  1. Blost: What exactly do you mean? It certainly compiles. Here’s a complete program:

    using System;
    using System.Text;

    delegate object ObjectFactory();

    class Test
    {
    public StringBuilder CreateStringBuilder()
    {
    return new StringBuilder();
    }

    public void Foo()
    {
    ObjectFactory factory = CreateStringBuilder;
    }

    static void Main(){}
    }

    That builds with C# 2.

    Jon

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s