当使用协方差和多个泛型参数时,强制转换为基接口

本文关键字:转换 接口 参数 泛型 方差 | 更新日期: 2023-09-27 18:18:29

我试图让所有这些映射器类有一个共同的基础:

// base
class BaseInput { public string BaseInputValue { get; set; } }
class BaseOutput { public string BaseOutputValue { get; set; } }
interface IMapper<InputType, out OutputType>
    where InputType : BaseInput
    where OutputType : BaseOutput
{
    OutputType Map(InputType input);
}
// example implementation
class Input : BaseInput { public string InputValue { get; set; } }
class Output : BaseOutput { public string OutputValue { get; set; } }
class MyMapper : IMapper<Input, Output>
{
    public Output Map(Input input)
    {
        // custom mapping done here to Output
        return new Output { /* mapping */ };
    }
}

创建一个新的映射器并将其分配给基本编译器的代码如下:

var myBaseMapper = (IMapper<BaseInput, BaseOutput>) new MyMapper();

但是我得到一个运行时错误:

无法将类型'MyMapper'的对象强制转换为类型'IMapper ' 2[UserQuery+BaseInput,UserQuery+BaseOutput]'.

如果我把IMapper减少到IMapper<out OutputType>,它工作得很好,但这需要在MyMapper.Map中做一个cast,这在每个映射器类中都有点烦人。此外,这使我失去了决定将哪个Mapper用于哪个BaseInput的信息,因此我必须在其他地方定义它。

在c#中这样做是不可能的,还是有类似的方法?如果没有,我将不得不重新考虑我的设计。

当使用协方差和多个泛型参数时,强制转换为基接口

您不能这样做,因为它没有意义,您的接口在InputType参数中是逆变的(或者,更确切地说,如果将in关键字添加到该参数中,它将是逆变的)。要了解为什么这没有意义,让我们看一下您的示例:

您希望IMapper<Input, Output>的实现可分配给IMapper<BaseInput, BaseOutput>。比如我们为BaseInput创建一个新的子类,命名为MoreInput:

class MoreInput : BaseInput
{
    public string LotsOfInput { get; set; }
}

好的,现在假设我们有一个方法,它的主体看起来像这样(并且你想要的实际上是有效的):

IMapper<BaseInput, BaseOutput> mapper = new MyMapper();
mapper.Map( new MoreInput() );

嗯,在这一点上没有什么问题:IMapper<BaseInput, BaseOutput>有一个Map方法,它接受BaseInput作为它的参数,MoreInput 是一个 BaseInput,所以我们对Map的调用是有效的。

但它不是,因为我们真正调用的Map方法期望Input作为其参数,而MoreInput不是Input。我们已经破坏了类型系统。

当编译器不允许隐式赋值时,它告诉你的是:转换不安全。编译器不能保证您期望的类型就是您得到的类型。