score:8

Accepted answer

In your second example, you're trying to convert NamedValue<bool> to NamedValue<TValue> -- this won't work, because the conversion has to be valid for any type argument. You can't convert NamedValue<bool> to NamedValue<int> or NamedValue<string> or NamedValue<AnythingElseOtherThanBool>.

One solution is to make NamedValueSource<TValue> abstract, as well as its GetNamedValues() method, and then create a class BooleanNamedValueSource : NamedValueSource<bool> class to use in your test.

In the linq case, the cast is not being done by the compiler; the cast occurs in a method that has already been compiled. All the compiler knows is that it is calling a method that takes IEnumerable<bool> and returns IEnumerable<TValue>. The specifics of that conversion are entirely invisible to the compiler.

score:12

UPDATE: This question was the subject of my blog on July 10th 2012; thanks for the great question!


Let's greatly simplify your complicated program.

public static class X
{
    public static V Cast<V>(object o) { return (V)o; }
}

class C<T> {}
class D<U>
{
    public C<U> value;
    public D()
    {
        this.value = X.Cast<C<U>>(new C<bool>());
    }
}

Now your second version, simplified:

class C<T> {}
class D<U>
{
    public C<U> value;
    public D()
    {
        this.value = (C<U>)(new C<bool>());
    }
}

OK, so now let's ask some questions.

Why does the second program fail at compile time?

Because there is no conversion from C<bool> to C<U> for arbitrary U. The compiler knows that the only way this could possibly succeed is is U is always bool, and therefore this program is almost certainly wrong! The compiler assumes that U is going to be something other than bool some of the time.

Why then does the first program succeed at compile time?

The compiler has no idea that a method named "X.Cast" should be treated like a cast operator for the purposes of error detection! As far as the compiler is concerned, the Cast method is a method that takes an object in and returns a V for whatever type parameter is provided for V. When compiling the body of the ctor of D, the compiler has no idea whatsoever that some method, which probably isn't even in this program to begin with, is going to try to do a cast that is going to fail unless U happens to be bool.

The compiler simply has no basis upon which to treat the first version as an error, even though it most certainly is a deeply wrong program. You'll have to wait until runtime to find out that your program is wrong.

Now let's make a third version of your program:

class C<T> {}
class D<U>
{
    public C<U> value;
    public D()
    {
        this.value = (C<U>)(object)(new C<bool>());
    }
}

This succeeds at compile time, so let's ask:

Why does this succeed at compile time?

For the exact same reason that the first one succeeds at compile time. When you inserted the cast you effectively said that you wanted the newly constructed C<bool> to be treated as an object, and so for the rest of the analysis of this expression, that expression is considered to be of type object, and not the more specific type C<bool>.

So then why is it legal to cast object to C<U> in this case? Or for that matter, to V in the first case?

It is legal to cast object to V because V could be the type of the object, a base type of the object or an interface implemented by the object, so the compiler allows the conversion because it figures there are a lot of ways it could possibly succeed.

Basically, it is legal to cast object to anything that you could convert to object. You cannot cast object to a pointer type, for instance, because no pointer type can be cast to object. But everything else is fair game.

By making the cast to object first, you are removing information from the purview of the compiler; you are saying "ignore the fact that you know this is always C<bool> for the purposes of error detection.


Related Articles