C# 4.0中的协变和逆变推断

8
当我们在C# 4.0中定义接口时,可以将每个泛型参数标记为inout。如果我们尝试将泛型参数设置为out并且会导致问题,则编译器会引发错误,不允许我们这样做。
问题:
如果编译器有推断出使用协变(out)和逆变(in)的有效方式,为什么我们还要标记接口呢?难道我们不能像以前一样定义接口,当我们在客户端代码中尝试使用它们时,如果我们尝试以不安全的方式使用它们,则引发错误即可吗?
示例:
interface MyInterface<out T> {
    T abracadabra();
}
//works OK

interface MyInterface2<in T> {
    T abracadabra();
}
//compiler raises an error.
//This makes me think that the compiler is cappable 
//of understanding what situations might generate 
//run-time problems and then prohibits them.

此外,

在相同情况下,Java不是也是这样做吗?据我所记,你只需要执行类似以下的操作:

IMyInterface<? extends whatever> myInterface; //covariance
IMyInterface<? super whatever> myInterface2; //contravariance

我是否混淆了事情?
谢谢
2个回答

8
如果编译器可以推断协变(out)和逆变(in)的有效用法,为什么我们还需要标记接口?
我不太确定我理解问题。我认为你在问两件事。
1)编译器能否推断出差异性注释?
2)为什么C#不支持像Java一样的调用点差异性?
第一个问题的答案是:
interface IRezrov<V, W> 
{
    IRezrov<V, W> Rezrov(IRezrov<W, V> x);
}

我邀请你尝试推断V和W上的所有合法可能差异注释。也许你会有惊喜。 如果你无法确定此方法的唯一最佳差异注释,为什么认为编译器可以呢? 更多原因在这里:

http://blogs.msdn.com/ericlippert/archive/2007/10/29/covariance-and-contravariance-in-c-part-seven-why-do-we-need-a-syntax-at-all.aspx

更一般地说:你的问题表明了谬误的推理。能够廉价地检查解决方案是否正确并不意味着有一种廉价的方法来找到正确的解决方案。例如,计算机可以轻松验证两个千位素数p和q乘积是否等于r的真假。这并不意味着很容易找到p和q,使得等式成立。编译器可以轻松检查方差注释是否正确或不正确;这并不意味着它可以在可能的数十亿个注释中找到一个正确的方差注释。
第二个答案是:C#不是Java。

1
@Gabe:C#支持声明点的协变性。Java支持调用点的协变性。调用点协变性无疑是一个有趣的想法,但对我来说,让类型根据特定位置的使用方式而变异,与其定义的行为方式不同,感觉有些奇怪。 - Eric Lippert
是的,我现在明白了Java使用中的问题。它的好处是不需要声明接口参数作为输入或输出,但是某些客户端现在可能会给它一些用途,如果我计划更新我的接口,则稍后可能无法支持这些用途。 - devoured elysium
实际上,在您的Rezrov示例中,只有4种情况:V和T都可以在内部或外部(或没有,但不计算)。还是我错了?无论如何,您不需要检查所有情况,只需检查客户端代码尝试运行的情况。也就是说,只有在尝试以某种方式使用接口编译代码时才会检查某些内容。 - devoured elysium
有趣的是,Java和C#在协变方面有如此不同的实现方式,考虑到Mads Torgersen对它们的影响。 - Gabe
@Gabe: 实际上,在 CLR v2 中添加泛型时,已经设计和实现了通用变异;我们只是没有在语言中表现出这个特性。Mads 是在相当晚的时候加入的。当然,我们确实充分利用了他的专业知识,确保在将其添加到 C# v4 中时规范得很好。 - Eric Lippert
显示剩余3条评论

0

好的,这里是我问的答案(来自Eric的回答): http://blogs.msdn.com/ericlippert/archive/2007/10/29/covariance-and-contravariance-in-c-part-seven-why-do-we-need-a-syntax-at-all.aspx

首先,我认为方差应该是您有意设计到接口或委托中的内容。让它在用户没有任何控制的情况下开始运作会与此目标相违背,并且可能引入破坏性变化。(更多内容将在以后发布的文章中介绍!)

这样做也意味着随着开发过程的进行并向接口添加方法,接口的方差可能会意外改变。这可能会在程序的其他地方引入意想不到的深远变化。

我决定在这里明确表述它,因为虽然他的链接确实回答了我的问题,但本帖子并没有。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接