views:

215

answers:

6

My question has to do with casting classes inside a generic type. While I realize that casting objects like List<string> to List<object> requires support for covariance to prevent adding object objects to a list that contains strings, I am wondering why a cast like below is not accepted by the compiler and whether it is solvable employing interfaces and co- or contravariance:

public class TypeA<T> {}

public class TypeB<T> {}

public class TypeC : TypeB<int> {}

class Program
{
    public static void MyMethod<OutputType>(TypeA<TypeB<OutputType>> Parameter) {}

    static void Main(string[] args)
    {
        TypeA<TypeC> Test = new TypeA<TypeC>();
        MyMethod<int>(Test);
    }
}

Compiling this results in an error:

Argument 1: cannot convert from 'ConsoleApplication1.TypeA<ConsoleApplication1.TypeC>' to 'ConsoleApplication1.TypeA<ConsoleApplication1.TypeB<int>>'.

even though TypeC is a direct descendent of TypeB<int>

A: 

But ConsoleApplication1.TypeA<ConsoleApplication1.TypeC> doesn't inherit from ConsoleApplication1.TypeA<ConsoleApplication1.TypeB<int>>

Rowland Shaw
A: 

Let's say I have I have a List object. I then cast it to List. List has a method called 'add' which takes a T argument. I add an Apple to it(since I have a List). However, in some places, my code still expects List, so some members of List are actually apples!!!

So, TypeA can not be cast to TypeA even if X inherits from Y.

luiscubal
How do you know an Apple is type T?
recursive
And what are X and Y? I actually don't understand any of this.
recursive
It was supposed to be an example of why inheritance was a bad idea. You can't cast List<Orange> to List<Fruit>, just like you can't cast List<X>(whatever X is) to List<Y> even if X extends Y.In this case, T was a generic type, and I was using a List<Apple>.
luiscubal
+2  A: 

Yet another generics covariance problem - see the answer I posted here. TypeA<TypeC> does not inherit off TypeA<TypeB<int>> in any way

thecoop
+1  A: 

Well, since C# 4.0 supports covariance for interfaces with generics, the following example can possibly solve the problem:

public interface ITypeA<out T> {}

public class TypeA<T> : ITypeA<T> {}

public class TypeB<T> {}

public class TypeC : TypeB<int> {}

class Program
{
    public static void MyMethod<OutputType>(ITypeA<TypeB<OutputType>> Parameter) {}

    static void Main(string[] args)
    {
        ITypeA<TypeC> Test = new TypeA<TypeC>();
        MyMethod<int>(Test);
    }
}

Note that type T can be used only for return values from methods in ITypeA.

AlexD
Turns out it was a lack of understanding on my part about how covariance works with interfaces. As Eric points out in his answer, TypeB and TypeC are analogous to the List casting case. A solution similar this actually solves my specific problem though, thanks very much.
Mark A.
+3  A: 

As other commenters have pointed out, despite the fact that TypeC is derived from TypeB<int>, it is not true that TypeA<TypeC> derives from TypeA<TypeB<int>>. However, you can probably get your code to work by adding an additional type parameter to MyMethod:

public static void MyMethod<DerivedB,OutputType>(TypeA<DerivedB> Parameter)
    where DerivedB : TypeB<OutputType> {}
kvb
+1  A: 

I am rather confused by your question, since the preamble to the question shows that you know what the answer is. You cannot convert a List<string> to a List<object> because then you could add a Giraffe to the list of objects that is really a list of strings, which is not typesafe.

If you replace "TypeA" with "List", TypeB<int> with "object" and "TypeC" with "string" then you have transformed your situation into one which you already know doesn't work for good reasons. It's not that the compiler magically knows something about List and just doesn't allow the List scenario -- rather, the compiler doesn't allow any such variance.

In C# 4 we're adding variance on some interface and delegate types that are known to be typesafe under variance. That's an "opt in" model -- you have to prove to the compiler that you're safe, and only then will we let you use variance.

Eric Lippert