views:

90

answers:

2

Consider, I have the following 3 classes / interfaces:

class MyClass<T> { }

interface IMyInterface { }

class Derived : IMyInterface { }

And I want to be able to cast a MyClass<Derived> into a MyClass<IMyInterface> or visa-versa:

MyClass<Derived> a = new MyClass<Derived>();
MyClass<IMyInterface> b = (MyClass<IMyInterface>)a;

But I get compiler errors if I try:

Cannot convert type 'MyClass<Derived>' to 'MyClass<IMyInterface>'   

I'm sure there is a very good reason why I cant do this, but I can't think of one.

As for why I want to do this - The scenario I'm imagining is one whereby you ideally want to work with an instance of MyClass<Derived> in order to avoid lots of nasty casts, however you need to pass your instance to an interface that accepts MyClass<IMyInterface>.

So my question is twofold:

  • Why can I not cast between these two types?
  • Is there any way of keeping the niceness of working with an instance of MyClass<Derived> while still being able to cast this into a MyClass<IMyInterface>?
+4  A: 

This does not work because C# only supports covariance on the type parameters of interfaces and delegates. If your type parameter exists only in output positions (i.e. you only return instances of it from your class and don't accept it as an argument) you could create an interface like this:

interface IClass<out T> { }
class MyClass<T> : IClass<T> { }

Which would allow you to do this:

IClass<Derived> a = new MyClass<Derived>();
IClass<IMyInterface> b = a;

Honestly that is about as close as you are going to get and this requires the C# 4 compiler to work.

Andrew Hare
+2  A: 

The reason you cannot do this in general is because most classes are not simple empty examples. They have methods:

class MyClass<T> 
{
    static T _storage;

    public void DoSomethingWith(T obj)
    {
        _storage = obj;
    }
}

interface IMyInterface { }

class Derived : IMyInterface { }

MyClass<Derived> a = new MyClass<Derived>();

Now, a has a method DoSomethingWith that accepts a Derived and stores it in a static variable of type Derived.

MyClass<IMyInterface> b = (MyClass<IMyInterface>)a;

If that was allowed, b would now appear to have a method DoSomethingWith that accepts anything that implements IMyInterface, and would then internally attempt to store it in a static variable of type Derived, because it's still really the same object referred to by a.

So now you'd have a variable of type Derived storing... who knows what.

Daniel Earwicker