C # generics - why do we need an explicit cast from a specific type back to T?

It bothered me a little. I am having trouble understanding why explicit casts are needed in the following code:

public static class CastStrangeness { public class A { } public class B : A { } public static void Foo<T>(T item) where T : A { // Works. A fromTypeParameter = item; // Does not compile without an explicit cast. T fromConcreteType = (T)fromTypeParameter; // Does not compile without an explicit cast. Foo<T>((T)fromTypeParameter); } public static void Bar(A item) { // Compiles. Foo<A>(item); } } 

It seems to me that T is guaranteed to be A, so, of course, the compiler could conclude that any instance of A is guaranteed to be assigned to T? Otherwise, I could not pass A or B to Foo (). So what am I missing?

PS. I tried to find endless keyword permutations on this, but each result seems to end up with a link to the common covariance and contravariant WRT interfaces :)

+4
source share
4 answers

Take the case:

 Foo(new B()); 

Your first appointment is in order:

 A fromtypeParameter = item; 

Since B: A.

But this assignment does not work:

 T fromConcreteType = fromTypeParameter; 

Because you can quite assign fromTypeParameter as:

 fromTypeParameter = new A(); 

Which you obviously cannot attribute to T (in this case, B). T is more specific than A, it can be obtained from A. Thus, you can go one way, but not another, without an explicit cast (which may not work).

+4
source

It seems to me that T is guaranteed to be A, so, undoubtedly, the compiler could conclude that any instance of A is guaranteed to be assigned to T?

Oh no.

string is an object. But

 string s = new object(); 

is illegal.

+4
source

All T can be A ... but not all A are T.

When you create a type that comes from A and passes it to a generic method, the compiler knows it as T, a derived type. If you do not return it, it does not know that you want A or T or any type of inheritance tree between T and A.

This logic applies whether you use generics or not.

 public class A {} public class B : A {} public class C: B {} A animal = new C(); C cat = animal; // wont compile as it does not know that A is a cat, // you have to cast even though it looks like // a cat from the new C(); 

Although you have a general restriction, this usually applies to the way you use the general method and prevents violation of the restriction. It does not apply to any link of a derived type through a base link. Although the compiler "may" understand this, it may not know that this was your intention, so it is better to be safe and not do it.

+3
source

Less subtle if you use more specific type and variable names:

 public class Animal {} public class Giraffe : Animal {} public static void Foo<TAnimal>(TAnimal animal) where TAnimal : Animal { Animal generalAnimal = animal; TAnimal possiblyMoreSpecificAnimal = (TAnimal) generalAnimal; // The above line only works with a cast because the compiler doesn't know // based solely on the variable type that generalAnimal is a more // specific animal. } 
0
source

All Articles