An interesting issue with generics, casting, and explicit/implicit operators

This is based on a recent thread on the MSDN forums. Someone had code that looked like this:

public class CustomObject<T>
    Object _obj;

    public Object Value
            return _obj;

    public static explicit operator CustomObject<T>(T obj)
        return new CustomObject<T>() { _obj = obj };

class App
    static void Main()
        var tmp1 = (CustomObject<Object>)true; // from bool

        var tmp2 = (CustomObject<Object>)12; // from int

        // The following code throws a System.InvalidCastException
        // Unable to cast object of type 'System.Object' to
        // type 'CustomObject`1[System.Object]'
        var tmp3 = (CustomObject<Object>)new Object(); // from object

The code compiles fine but the third conversion results in an InvalidCastException. And he was quite puzzled by it.

The answer is simple if you think about it, and the exception that’s thrown is a dead giveaway. When you have an object there, the compiler sees that as a downcast (casting from base to a more derived type). The reason is that CusObject<T> is derived from Object (implicitly) and so when the type being converted is Object, it will not call the explicit (or implicit) conversion operator, instead it will generate a castclass IL instruction which will obviously fail.

To make that more clear, forget the “generics” and take this example:

class Base

class Derived : Base
    public static explicit operator Derived(Base b)
        return new Derived();

That will not compile and you’ll see this:

Error 1 ‘Derived.explicit operator Derived(Base)’: user-defined conversions to or from a base class are not allowed

Of course when you use a generic parameter, the compiler cannot anticipate that you’d try doing this, and so it will let you declare the operator. And when you later have an ambiguous situation where you cast from base to derived, it will simply ignore the explicit operator and instead do a downcast.

In the MSDN thread, pointed out the appropriate section in the language spec that talks about this. For those interested it’s 10.10.3. Quoting below:

However, it is possible to declare operators on generic types that, for particular type arguments, specify conversions that already exist as pre-defined conversions.

In cases where a pre-defined conversion exists between two types, any user-defined conversions between those types are ignored.


Leave a Reply

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

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

Google+ photo

You are commenting using your Google+ 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