C#generics – 为什么从具体类型回到T需要显式转换?

这困扰了我一段时间。 我在理解为什么在以下代码中需要显式强制转换时有点麻烦:

public static class CastStrangeness { public class A { } public class B : A { } public static void Foo(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)fromTypeParameter); } public static void Bar(A item) { // Compiles. Foo(item); } } 

在我看来,T保证是A,所以编译器肯定会推断A的任何实例都可以保证可以分配给T? 否则,我将无法将A或B传递给Foo()。 那么我错过了什么?

PS。 我已经尝试在此搜索关键字的无穷无尽的排列,但每个结果似乎最终指的是协方差和逆变WRT通用接口:)

以下为例:

 Foo(new B()); 

你的第一个任务是好的:

 A fromtypeParameter = item; 

从B:A开始

但是这个任务不合适:

 T fromConcreteType = fromTypeParameter; 

因为您很可能已将fromTypeParameter指定为:

 fromTypeParameter = new A(); 

你显然无法转换为T(在这个例子中是B)。 T比A更具体,它可以从A派生。所以你可以走一条路而不是另一条路,没有明确的演员(可能会失败)。

在我看来,T保证是A,所以编译器肯定会推断A的任何实例都可以保证可以分配给T?

好吧,不。

string是一个对象。 但

 string s = new object(); 

是非法的。

所有的T都可能是A ……但不是所有的A都是T.

当您创建一个派生自A并将其传递给generics方法的类型时,编译器将其视为T(派生类型)。 如果你没有将它强制转换,它不知道你想要A或T,或T和A之间的inheritance树中的任何类型。

无论您是否使用generics,此逻辑都适用。

 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(); 

即使您具有generics约束,这也倾向于应用generics方法的使用方式,并防止约束被破坏。 它不适用于通过基本引用引用派生类型的任何引用。 虽然编译器“可以”弄清楚它可能不知道你的意图是什么,所以最好安全而不是推断它。

如果您使用更具体的类型和变量名称,则不那么微妙:

 public class Animal {} public class Giraffe : Animal {} public static void Foo(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's type that generalAnimal is a more // specific animal. }