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. }