如何向上转换泛型类型参数

本文关键字:转换 泛型类型参数 | 更新日期: 2023-09-27 18:21:20

在这种情况下使用反射时,创建的类型可以是许多泛型类型。

BaseStepHandler<BaseStepDataModel> activator = (BaseStepHandler<BaseStepDataModel>)Activator.CreateInstance(....);

创建的实例可以是BaseStepDataModel的所有子级。

BaseStepHandler<OneDataModel>
OR
BaseStepHandler<TwoDataModel>

OneDataModel和TwoDataModel正在扩展BaseStepDataModel。

这是我得到的例外:

无法强制转换类型为"…."的对象。。。。GlobalOnBoardingStepOneHandler"要键入"。。。。BaseStepHandler `1[….BaseStepDataModel]'.

这是GlobalOnBoardingStepOneHandler的声明。

public class GlobalOnBoardingStepOneHandler : BaseStepHandler<GlobalOnBoardingStepOneDataModel>{}

如何向上转换泛型类型参数

这里的问题是,对于具体类型的通用参数,您期望逆变差协方差

基本上,使用具体类型永远不会实现目标,但有一个解决方法。

你可以设计这样的标记界面:

public interface IBaseStepHandler<out T> // "out" marks T as covariant
   where T : BaseDataModel // Do you have a model base type? ;)
{
     // Declare members here
}

在我说"在这里声明成员"的地方,只声明这些成员,它们是具体基类的一部分(我说的是"BaseStepHandler")。

之后,在基类BaseStepHandler中实现此接口。

现在,你可以做你想做的事:

IBaseStepHandler<BaseDataModel> some = new WhateverBaseStepHandlerClass(); 
// This is possible because T generic parameter is covariant and it can be casted to `BaseDataModel`, or if you don't provide a `T` generic parameter constraint, you could cast it to `IBaseStepHandler<object>` too!

要了解有关协方差的更多信息,请访问以下链接:http://msdn.microsoft.com/en-us/library/ee207183.aspx

因为GlobalOnBoardingStepOneHandler继承自BaseStepHandler<GlobalOnBoardingStepOneDataModel>,而不是BaseStepHandler<BaseStepDataModel>,所以您得到了异常。这可能是.NET泛型中最常见的错误。泛型对于类型参数不是协变的。

参见:

C#:转换为具有基本类型的通用接口

http://blogs.msdn.com/b/csharpfaq/archive/2010/02/16/covariance-and-contravariance-faq.aspx

http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx

http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-two.aspx

http://blogs.msdn.com/b/ericlippert/archive/2007/10/17/covariance-and-contravariance-in-c-part-two-array-covariance.aspx

http://blogs.msdn.com/b/ericlippert/archive/2007/10/19/covariance-and-contravariance-in-c-part-three-member-group-conversion-variance.aspx

等等。。。

问题是您假设因为GlobalOnBoardingStepOneDataModel继承自BaseStepDataModel,所以GlobalOnBoardingStepOneHandler继承自BaseStepHandler<BaseStepDataModel>。事实并非如此,所以你不能从一个演员换到另一个演员。

例如,考虑以下内容:

var myListOfStrings = new List<String>();
// By your logic, this should compile (it doesn't):
var myListOfObjects = ((List<Object>)myListOfStrings);
// But if it did, this would be possible:
myListOfObjects.Add(1); // Holy cow, I just added an integer to a list of strings!  What is the world coming to?

现在,这对于恢复Java程序员来说非常令人困惑,因为这在Java中是可能的。在Java中,您有类型擦除,因此在运行时List<String>实际上只是List<Object>,因此您可以将其强制转换为任何您喜欢的类型,并将任何您想要的内容放入其中。因为CLR使用的是具体化的泛型,而不是类型擦除,所以List<String>实际上是一个与List<Object>List<Integer> 不同的类型