11

我有一个简单的工厂方法,它提供了一个基于提供的泛型类型参数的具体实现实例。如果具体类继承自具有类型参数的公共抽象基类,我无法强制转换它们。编译器告诉我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(). 这是编译器缺陷,还是故意设计决定以不同的方式处理具有类型参数的抽象类和接口?

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

4

3 回答 3

9

这是无法证明的,因为通用代码需要在所有可能的情况下都可以工作(使用相同的 IL),例如T,没有什么可说Car : VehicleBase<float>的。if编译器不会过度分析检查的事实- 静态检查器单独处理每个语句T不会尝试理解条件的因果关系。CarWheel

要强制它,请object在中间投射:

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

然而!您的方法并不是真正的“通用”。

于 2012-06-26T06:30:42.133 回答
6

这既不是编译器缺陷,也不是故意的决定。泛型类的类型参数既不是协变的也不是逆变的,即同一个泛型类的特化之间没有继承关系。从文档:

在 .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();
        }
    }
}

查看“泛型中的协方差和逆变”以获取更多信息和示例。

C# FAQ 博客中还有一个协变和逆变常见问题解答,其中包含更多信息,以及一个11 部分的系列!埃里克·利珀特 ( Eric Lippert ) 的主题

于 2012-06-26T06:44:57.110 回答
3

这似乎有效:

return new Car() as VehicleBase<T>;

为什么会这样:
由于泛型类型实例VehicleBase<T>不相关,因此无法证明强制转换它们可以工作:

在此处输入图像描述

如果T是 type Blah,则演员表将不起作用。你不能回到 object 然后再选择另一个分支(毕竟 C# 中没有多重继承)。

通过将其转换回object之前,您再次打开了转换可能起作用的可能性,因为可能仍有一条通往VehicleBase<CarWheel>. 当然,一个界面可以出现在下面这棵树的任何地方object,所以它也应该可以工作。

于 2012-06-26T06:30:49.547 回答