让我们从以下分类法开始
public abstract class Automobile { }
public class Automobile<T> : Automobile where T : Automobile { }
public class Car : Automobile<Car> { }
public class Truck : Automobile<Truck> { }
public class SmartAutomobile<T> : Automobile<T>
{
public T MyAutomobile { get; set; }
public SmartAutomobile(SmartTechnology smart)
{
// Cannot implicitly convert Automobile to T
this.MyAutomobile = AutomobileFromSmart(typeof(T), smart);
}
public static Automobile AutomobileFromSmart(Type type, SmartTechnology smart)
{
if (type == typeof(Car))
return new Car { /* ... */ };
else
throw new NotImplementedException("Car type " + type.FullName + " not recognized");
}
}
public class SmartTechnology { }
从评论中可以看出,编译器说它无法将Automobile
转换为T
的构造函数中的SmartAutomobile<T>
。怎么会这样?编译器应该知道T
,因为Automobile<T>
中的约束, 是Automobile
。
如果我尝试明确地施展它
this.MyAutomobile = AutomobileFromSmart(typeof(T), smart) as T;
我收到编译错误
类型参数“T”不能与“as”运算符一起使用,因为它没有类类型约束,也没有“类”约束
现在,如果我还在where
SmartAutomobile<T>
约束
public class SmartAutomobile<T> : Automobile<T> where T : Automobile
编译器不显示任何错误
但是如果我删除显式的强制转换:
this.MyAutomobile = AutomobileFromSmart(typeof(T), smart);
无法隐式将汽车转换为T 错误再次出现。
编译器如何才能实现where
约束强制T
成为Automobile
?
答案 0 :(得分:5)
怎么可能解释器没有意识到哪里约束迫使T成为汽车?
不,它迫使T
派生自 Automobile
。由于向下投射并不总是安全的,因此您无法隐式地从Automobile
投射到T
。如果T
为Car
,但AutomobileFromSmart
返回了Truck
,则转换会在运行时失败。您可以显式强制转换(或使用as
),它告诉编译器“我知道我在做什么,这个演员在运行时是安全的”。
答案 1 :(得分:4)
差异。
您的约束表明来自T
的{{1}} 派生。这并不意味着Automobile
是T
- 它也可能是从Automobile
派生的类。现在你的情况是,应该始终属于Automobile
类型的变量的类型为MyAutomobile
- 这完全违反了类型系统。
因此,您可以将Automobile
传递给可以传递T
的任何地方,但不会反过来 - 就像您可以通过Automobile
而不是Label
一样},但反之亦然。
您的第二个问题是通用约束不会被继承。你必须在他们需要的任何地方提到它们。这就是为什么Control
仍然需要SmartAutomobile<T>
中同样的约束的原因。