在实现接口方法时,为什么“协方差”和“逆变”的概念适用?

用例是这样的:

public class SomeClass: IClonable { // Some Code //Implementing interface method Public object Clone() { //Some Clonning Code } } 

现在我的问题是“为什么不能使用”SomeClass(因为它来自objec)“作为Clone()方法的返回类型,如果我们考虑Funda的协方差和逆变。

有人可以解释一下这个实现Microsoft ????的原因

接口实现方差的非破坏实现必须在返回类型中是协变的, 并且在参数类型中必须是逆变的

例如:

 public interface IFoo { object Flurp(Array array); } public class GoodFoo : IFoo { public int Flurp(Array array) { ... } } public class NiceFoo : IFoo { public object Flurp(IEnumerable enumerable) { ... } } 

根据“新”规则,两者都是合法的,对吧? 但是这个怎么样:

 public class QuestionableFoo : IFoo { public double Flurp(Array array) { ... } public object Flurp(IEnumerable enumerable) { ... } } 

有点难以分辨哪个隐式实现在这里更好。 第一个是参数类型的精确匹配,但不是返回类型。 第二个是返回类型的精确匹配,但不是参数类型。 我倾向于第一个,因为使用IFoo接口的人只能给它一个Array ,但它仍然不完全清楚。

到目前为止,这并不是最糟糕的。 如果我们这样做会怎么样:

 public class EvilFoo : IFoo { public object Flurp(ICollection collection) { ... } public object Flurp(ICloneable cloneable) { ... } } 

哪一个获奖? 这是一个完全有效的重载,但ICollectionICloneable彼此无关,而Array实现了它们。 我在这里看不到明显的解决方案。

如果我们开始向接口本身添加重载,情况会变得更糟:

 public interface ISuck { Stream Munge(ArrayList list); Stream Munge(Hashtable ht); string Munge(NameValueCollection nvc); object Munge(IEnumerable enumerable); } public class HateHateHate : ISuck { public FileStream Munge(ICollection collection); public NetworkStream Munge(IEnumerable enumerable); public MemoryStream Munge(Hashtable ht); public Stream Munge(ICloneable cloneable); public object Munge(object o); public Stream Munge(IDictionary dic); } 

祝你好运,试图解开这个谜团而不会疯狂。

当然,如果断言接口实现应该只支持返回类型方差而不支持参数类型方差,那么所有这些都没有实际意义。 但是几乎每个人都会认为这样的半实现完全被破坏并开始发送垃圾邮件报告,所以我认为C#团队不会这么做。

我不知道这是否是它今天在C#中不受支持的官方原因,但它应该是它可能导致的那种“只写”代码的一个很好的例子,也是C#团队设计的一部分哲学是试图阻止开发人员编写糟糕的代码。

让我重新解释一下这个问题:

诸如C ++之类的语言允许覆盖方法具有比重写方法更具体的返回类型。 例如,如果我们有类型

 abstract class Enclosure {} class Aquarium : Enclosure {} abstract class Animal { public virtual Enclosure GetEnclosure(); } 

那么这在C#中是不合法的,但是等效代码在C ++中是合法的:

 class Fish : Animal { public override Aquarium GetEnclosure() { ... 

C ++的这个特性叫什么?

该function称为“返回类型协方差”。 (正如另一个答案指出的那样,也可以支持“forms参数类型逆转”,尽管C ++没有。)

为什么C#不支持它?

正如我多次指出的那样,我们不必提供不支持function的原因; 所有function的默认状态为“不支持”。 只有当大量的时间和精力投入到实现function得到支持时。 相反,实现的function必须有它们的原因,并且考虑到制作它们需要花费多少成本。

也就是说,有两个重要的“反对”这个function是防止它完成的主要因素。

  1. CLR不支持它。 为了完成这项工作,我们基本上必须实现完全匹配的方法,然后创建一个调用它的辅助方法。 这是可行的,但它会变得混乱。

  2. 安德斯认为这不是一个很好的语言function。 安德斯是首席架构师,如果他认为这是一个糟糕的特征,很可能它不会完成。 (现在,请注意,我们认为命名和可选参数也不值得花费,但最终确实完成了。有时很明显你必须咬紧牙关并实现一个你并不喜欢的function美学,以满足现实世界的需求。)

简而言之,它肯定会有用,这是一个经常被要求的function。 但是,我们不太可能这样做。 该function的好处不支付其费用; 它使方法的语义分析变得相当复杂,我们没有简单的方法来实现它。

您必须完全按照界面中的方式实现接口的方法。 ICloneable的Clone方法返回一个对象,因此SomeClass也必须返回一个对象。 但是,您可以在SomeClass的Clone方法中返回SomeClass实例而没有任何问题,但方法定义必须与接口匹配:

 public class SomeClass: IClonable { // Some Code //Implementing interface method Public object Clone() { SomeClass ret = new SomeClass(); // copy date from this instance to ret return ret; } } 

在解释C#决策背后的原因方面,微软的Eric Lippert写了很多关于C#中Contra / CoVariance的文章…这里是他博客上的标签列表: http : //blogs.msdn.com/ericlippert/archive/tags/协方差+和+逆变/ Default.aspx的

[编辑]特定于您的问题,这可能是正确的post.. http://blogs.msdn.com/ericlippert/archive/2007/10/26/covariance-and-contravariance-in-c-part-five-接口variance.aspx

它看起来像是他们可以使用generics的东西,但似乎有一个很好的理由为什么他们没有。

这里有人谈到:

http://bytes.com/topic/c-sharp/answers/469671-generic-icloneable

基本上,通用接口允许: public class MyClass : IClonable

还允许: public class MyClass : IClonable

这并没有真正提供任何好处,并可能使事情混乱。

根据C#规范,在覆盖或实现接口方法时,必须使用具有相同签名的方法。 请记住,Microsoft不拥有C#。 他们的C#编译器只是它们的实现。 那么为什么规范会以这种方式做事呢? 我只能猜测,但我怀疑这是为了便于实施。