28

我正在尝试转换一个逆变委托,但由于某种原因,我只能使用“as”运算符来实现。

interface MyInterface { }
delegate void MyFuncType<in InType>(InType input);

class MyClass<T> where T : MyInterface
{
    public void callDelegate(MyFuncType<MyInterface> func)
    {
        MyFuncType<T> castFunc1 = (MyFuncType <T>) func; //Error
        MyFuncType<T> castFunc2 = func as MyFuncType<T>; 
        MyFuncType<T> castFunc3 = func is MyFuncType<T> ? (MyFuncType<T>)func : (MyFuncType<T>)null; //Error
    }
}

castFunc2 工作正常,但 castFunc1 和 castFunc3 导致错误:

Cannot convert type 'delegateCovariance.MyFuncType<myNamespace.MyInterface>' to myNamespace.MyFuncType<T>'

关于 as 运算符的MSDN 文章指出 castFunc2 和 castFunc3 是“等效的”,所以我不明白它们中的一个如何会导致错误。另一个让我困惑的部分是,将 MyInterface 从接口更改为类可以消除错误。

谁能帮我理解这里发生了什么?谢谢!

4

3 回答 3

15

添加一个约束,使得 T 必须是一个类。

class MyClass<T> where T: class, MyInterface

这为编译器提供了足够的信息来知道 T 是可转换的。您也不需要显式转换。

差异仅适用于引用类型。允许 T 是一个值类型,没有约束,这会破坏编译器证明 T 与逆变兼容的能力。

第二条语句起作用的原因是因为as实际上可以执行空转换。例如:

class SomeClass { }
interface SomeInterface { }
static void Main(string[] args)
{
   SomeClass foo = null;
   SomeInterface bar = foo as SomeInterface;
}

Foo显然不能直接转换为SomeInterface,但它仍然成功,因为仍然可以进行空转换。您的 MSDN 参考对于大多数情况可能是正确的,但是生成的 IL 代码非常不同,这意味着从技术角度来看它们是根本不同的。

于 2012-12-11T05:54:05.273 回答
4

Eric Lippert 在他最近的帖子中对这个问题给出了很好的解释:“is”运算符之谜,第一部分“is”运算符之谜,第二部分

这种行为背后的主要理由如下:“is”(或“as”)运算符与强制转换不同。如果相应的强制转换是非法的,“as”运算符可能会导致非空结果事件,当我们处理类型参数时尤其如此。

基本上,cast在您的情况下,运算符意味着(如 Eric 所说)“我知道这个值是给定类型的,即使编译器不知道,编译器应该允许它”或“我知道这个值不是给定类型;生成专用的、特定于类型的代码,以将一种类型的值转换为不同类型的值。”

后面的案例处理像双整数转换这样的值转换,我们可以在当前上下文中忽略这个含义。

泛型类型参数在第一个上下文中也不合逻辑。如果您正在处理泛型类型参数,那么为什么不使用泛型类型参数清楚地说明这个“合同”?

我不是 100% 确定你想要实现什么,但你可以从你的方法中省略特殊类型并自由使用泛型参数:

class MyClass<T> where T : MyInterface
{
    public void callDelegate(Action<T> func)
    {
    }
}

class MyClass2
{
    public void callDelegate<T>(Action<T> func)
        where T : MyInterface
    {
    }
}

否则,您应该使用as检查 null 而不是类型检查的运算符。

于 2012-12-11T06:54:21.417 回答
-1

Your class says that T implements MyInterface because MyInterface is not a instance type. Therefore, MyFuncType<T> is not guaranteed to be MyFuncType<MyInterface>. It could be MyFuncType<SomeType> and SomeType : MyInterface, but that wouldn't be the same as SomeOtherType : MyInterface. Make sense?

于 2012-12-11T04:14:19.750 回答