无法使用类型参数将派生类型强制转换为基类抽象类

时间:2012-06-26 06:26:51

标签: c# compiler-errors

我有一个简单的工厂方法,它根据提供的泛型类型参数提供具体的实现实例。如果具体类继承自具有类型参数的公共抽象基类,则无法强制转换它们。编译器告诉我Error 2 Cannot convert type 'Car' to 'VehicleBase<T>'。如果我用抽象类替换具有相同类型参数的接口,或者如果我从抽象类中删除泛型类型参数,它可以正常工作。

interface IWheel
{
}

class CarWheel : IWheel
{
}

abstract class VehicleBase<T>
{
}

class Car : VehicleBase<CarWheel>
{
}

class VehicleFactory
{
    public static VehicleBase<T> GetNew<T>()
    {
        if (typeof(T) == typeof(CarWheel))
        {
            return (VehicleBase<T>)new Car();
        }
        else
        {
            throw new NotSupportedException();
        }
    }
}

无法在(VehicleBase<T>)new Car()上编译。这是一个编译器缺陷,或者这是一个故意的设计决策,以不同的方式处理抽象类和接口与类型参数?

作为一种解决方法,我总是可以使抽象类实现一个接口并将其用作我的工厂方法的返回值,但我仍然想知道为什么会发生这种行为。

3 个答案:

答案 0 :(得分:9)

这不是可证明的,因为通用代码需要为每个可能的T工作(使用相同的IL),并且没有什么可说的例如Car : VehicleBase<float>。编译器不会过度分析if检查母猪TCarWheel的事实 - 静态检查器单独处理每个语句 ,它不会试着了解条件的因果关系。

要逼迫它,请转到中间的object

return (VehicleBase<T>)(object)new Car();

然而!你的方法本身并不是“通用的”。

答案 1 :(得分:6)

这既不是编译器缺陷也不是故意的决定。泛型类的类型参数是neither covariant nor contravariant,即同一泛型类的特化之间没有继承关系。来自文档:

  

在.NET Framework第4版中,变体类型参数仅限于通用接口和通用委托类型。

这意味着将编译以下代码,因为它使用的是接口而不是抽象类:

interface IWheel
{
}

class CarWheel : IWheel
{
}

interface IVehicleBase<T> 
{
}

class Car : IVehicleBase<CarWheel>
{
}

class VehicleFactory
{
    public static IVehicleBase<T> GetNew<T>() 
    {
        if (typeof(T) == typeof(CarWheel))
        {
            return (IVehicleBase<T>)new Car();
        }
        else
        {
            throw new NotSupportedException();
        }
    }
}

查看“Covariance and Contravariance in Generics”了解更多信息和示例。

C#常见问题解答博客中还有一个Covariance and Contravariance FAQ,其中包含更多信息,11-part series!

主题为Eric Lippert

答案 2 :(得分:3)

这似乎有效:

return new Car() as VehicleBase<T>;

我的猜测为什么会这样:
由于VehicleBase<T>的泛型类型实例不相关,因此无法证明转换它们可以起作用:

enter image description here

如果T的类型为Blah,则广告投放无效。你不能回到对象然后拿另一个分支(毕竟在C#中没有多重继承)。

之前将其强制转换回object,您再次打开了演员可能有效的可能性,因为可能仍有一条路径可以追溯到VehicleBase<CarWheel>。当然,界面可以在object下面的树中的任何地方出现,这样也可以。