如何转发泛型类型参数

时间:2012-01-24 15:41:50

标签: c# generics

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

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>{}

2 个答案:

答案 0 :(得分:4)

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

请参阅:

C#: cast to generic interface with base type

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继承自BaseStepDataModelGlobalOnBoardingStepOneHandler继承自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>

分开且不同的类型

答案 1 :(得分:3)

这里的问题是你期望具体类型通用参数的 contravariance covariance

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

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

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