【问题标题】:Why is it needed to cast a generic type when a where constraint should be enough当 where 约束应该足够时,为什么需要强制转换泛型类型
【发布时间】:2016-12-02 22:35:10
【问题描述】:

让我们从以下分类开始

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

从评论中可以看出,编译器说它无法在SmartAutomobile&lt;T&gt; 的构造函数中将Automobile 转换为T。怎么会这样?编译器应该知道 T,由于 Automobile&lt;T&gt; 中的约束, Automobile

如果我尝试显式转换它

this.MyAutomobile = AutomobileFromSmart(typeof(T), smart) as T;

我得到编译器错误

类型参数“T”不能与“as”运算符一起使用,因为它没有类类型约束,也没有“类”约束

现在,如果我还在SmartAutomobile&lt;T&gt; 中定义where 约束

public class SmartAutomobile<T> : Automobile<T> where T : Automobile

编译器没有显示任何错误

但如果我删除显式演员:

this.MyAutomobile = AutomobileFromSmart(typeof(T), smart);

Cannot implicitly convert Automobile to T错误再次出现。

编译器怎么可能没有意识到where 约束强制T 成为Automobile

【问题讨论】:

  • 您没有将其限制为class
  • 既然你想投它实际上投它。你不是在表演。
  • 顺便说一句,C#有编译器而不是解释器

标签: c#


【解决方案1】:

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

不,它强制T 派生自 Automobile。而且由于向下转换并不总是安全的,因此您不能从Automobile 隐式转换为T。如果TCar,但AutomobileFromSmart 返回Truck,则转换将在运行时失败。您可以显式强制转换(或使用as),它会告诉编译器“我知道自己在做什么,并且这种强制转换在运行时是安全的”)。

【讨论】:

  • 所以在这个特定的实现中,将其转换为T是完全安全的?
  • 就你现在所拥有的而言,是的,但是编译器没有深入挖掘到知道这一点。加上方法是public,所以不能保证其他人不会用不同的类型调用它。
【解决方案2】:

方差。

您的约束表明T派生Automobile。这并不意味着T 一个Automobile - 它也可以是一个派生自Automobile 的类。现在您遇到了这样一种情况,即应该始终为 MyAutomobile 类型的变量改为 Automobile 类型 - 这完全违反了类型系统。

因此,您可以在任何可以传递 Automobile 的地方传递 T,但反之亦然 - 就像您可以传递 Label 而不是 Control,但反之则不行。

您的第二个问题是通用约束没有被继承。您必须在需要它们的任何地方提及它们。这就是为什么SmartAutomobile&lt;T&gt; 仍然需要与Automobile&lt;T&gt; 相同的约束。

【讨论】:

    猜你喜欢
    • 2011-12-28
    • 2017-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-08
    • 1970-01-01
    • 2015-04-05
    • 1970-01-01
    相关资源
    最近更新 更多