将MyClass <string>转换为MyClass <object>在运行时失败</object> </string>

时间:2013-07-07 13:09:39

标签: c# generics

在开发一个应该处理各种泛型lambda表达式的类时,我遇到了一个相当熟悉的漏洞:我有一个MyClass<T>类,我试图将MyClass<string>强制转换为MyClass<object>,像这样:

MyClass<string> myClassString = GetMyClass(); // Returns MyClass<String>
MyClass<object> myClassObject = myClassString;

这样做会返回一个编译错误,说明两种类型之间没有隐式转换,但确实存在显式转换。所以我添加了显式转换:

MyClass<object> myClassObject = (MyClass<object>)myClassString;

现在代码已编译,但在运行时失败,声称转换是非法的。

我使用的是Visual Studio 2012,代码是使用C#5编译的可移植类库项目的一部分。

为了确保,我替换了MyClass IList - 出现了相同的行为 - 允许显式转换,但在运行时失败。

为什么呢?为什么编译器会接受这个?如果它在运行时失败,那么显式转换的重点是什么?

4 个答案:

答案 0 :(得分:5)

为了允许演员表,您需要将其标记为协变。但是,协方差仅允许用于接口(和委托)。这看起来像是:

interface MyInterface<out T> ...

您可以编译显式强制转换的原因可能是编译器假定GetMyClass()的返回值可能是MyClass<object>。但没有宣言就很难说。

答案 1 :(得分:3)

为了能够将MyClass<string>投射到MyClass<object>,您需要完成以下任务:

  • MyClass<T>必须是一个界面,例如IMyClass<T>
  • 您需要将out关键字添加到类型参数T,使类型参数协变
  • 类型参数T可能只出现在界面成员的输出位置。

例如:

public interface IMyClass<out T>
{
    T GetItem();    // T in an output position
}

现在你可以施展它了:

IMyClass<string> myClassString;
IMyClass<object> myClassObject = (IMyClass<object>)myClassString;

答案 2 :(得分:0)

如果您有A类和B类,并且想要将B转换为A,则必须满足以下条件之一:

  • B派生/实施A,反之亦然
  • 有一个从B到A
  • 定义的显式强制转换运算符

在您的情况下,上述情况均属实,因此演员表无效。

如果您的类实现IEnumerable,您可以使用扩展方法

using System.Linq
...
MyClass<Object> asObject = GetMyClass().Cast<Object>();

否则,编写一个执行转换的显式运算符或函数:

public MyClass<Base> ConvertToBase<Base, Derived>(MyClass<Derived>) where Derived : Base
{
    // construct and return the appropriate object
}

答案 3 :(得分:-2)