如何向上转换泛型类型参数
本文关键字:转换 泛型类型参数 | 更新日期: 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>
不同的类型