铸造MyClass<;字符串>;到MyClass<;对象>;在运行时失败

本文关键字:gt lt MyClass 运行时 失败 字符串 铸造 对象 | 更新日期: 2023-09-27 18:21:27

在开发一个应该处理各种泛型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——出现了相同的行为——允许显式转换,但在运行时失败。

为什么?编译器为什么接受这个?如果显式转换在运行时失败,它有什么意义?

铸造MyClass<;字符串>;到MyClass<;对象>;在运行时失败

为了允许强制转换,您需要将其标记为协变。但是,协方差只允许用于接口(和委托)。这看起来像:

interface MyInterface<out T> ...

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

要将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;

如果您有类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
}

使用Conversion运算符实现将类强制转换为其他类

http://msdn.microsoft.com/en-us/library/85w54y0a.aspx

http://msdn.microsoft.com/en-us/library/09479473(v=vs.80).aspx