当where约束应该足够时,为什么需要转换泛型类型

时间:2016-12-01 15:18:57

标签: c#

让我们从以下分类法开始

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

2 个答案:

答案 0 :(得分:5)

  

怎么可能解释器没有意识到哪里约束迫使T成为汽车?

不,它迫使T 派生自 Automobile。由于向下投射并不总是安全的,因此您无法隐式地从Automobile投射到T。如果TCar,但AutomobileFromSmart返回了Truck,则转换会在运行时失败。您可以显式强制转换(或使用as),它告诉编译器“我知道我在做什么,这个演员在运行时是安全的”。

答案 1 :(得分:4)

差异。

您的约束表明来自T的{​​{1}} 派生。这并不意味着Automobile T - 它也可能是从Automobile派生的类。现在你的情况是,应该始终属于Automobile类型的变量的类型为MyAutomobile - 这完全违反了类型系统。

因此,您可以将Automobile传递给可以传递T的任何地方,但不会反过来 - 就像您可以通过Automobile而不是Label一样},但反之亦然。

您的第二个问题是通用约束不会被继承。你必须在他们需要的任何地方提到它们。这就是为什么Control仍然需要SmartAutomobile<T>中同样的约束的原因。