C# “为什么是”的概念;协方差;及;对冲;在实现接口的方法时是否适用?

C# “为什么是”的概念;协方差;及;对冲;在实现接口的方法时是否适用?,c#,.net,covariance,contravariance,C#,.net,Covariance,Contravariance,用例是这样的: public class SomeClass : ICloneable { // Some Code // Implementing interface method public object Clone() { // Some Clonning Code } } 现在我的问题是,如果我们考虑协方差和逆变的FunDA,为什么不可能使用“SomeClass(因为它是来自对象)”作为返回类型的克隆(?)方法?< /P> 有

用例是这样的:

public class SomeClass : ICloneable
{
    // Some Code

    // Implementing interface method
    public object Clone()
    {
        // Some Clonning Code
    }
}
现在我的问题是,如果我们考虑协方差和逆变的FunDA,为什么不可能使用“SomeClass(因为它是来自对象)”作为返回类型的克隆(?)方法?< /P>
有人能给我解释一下Microsoft实现的原因吗???

你必须完全按照接口中的方法来实现接口的方法。ICloneable的Clone方法返回一个对象,因此SomeClass也必须返回一个对象。但是,您可以在SomeClass的克隆方法中毫无问题地返回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#规范,在重写或实现接口方法时,必须使用具有相同签名的方法。请记住,微软并不拥有C。他们的C#编译器只是它的实现。那么为什么规范会这样做呢?我只能猜测,但我怀疑这是为了便于实现。

在解释C#决策背后的原因方面,微软的埃里克·利珀特(Eric Lippert)写了很多解释C#中的反/协方差的文章。。。以下是他博客上的标签列表:

[编辑]
针对您的问题,这可能是正确的帖子

这看起来像是他们可以使用泛型的事情,但似乎有一个很好的理由说明他们没有这样做

这里讲的是:

基本上,通用接口允许:
公共类MyClass:IClonable

还将允许:
公共类MyClass:IClonable


这实际上并没有提供任何好处,而且可能会使事情变得混乱。

接口实现差异的非中断实现必须在返回类型中是协变的,在参数类型中是逆变的

例如:

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
接口,都只能给它一个
数组,但它仍然不完全清楚

到目前为止,这还不是最糟糕的。如果我们改为这样做呢:

public class EvilFoo : IFoo
{
    public object Flurp(ICollection collection) { ... }
    public object Flurp(ICloneable cloneable) { ... }
}
哪一个得奖?这是一个完全有效的重载,但是
ICollection
ICloneable
彼此无关,而
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);
}
祝你好运,在不发疯的情况下解开这个谜团

当然,如果您断言接口实现应该只支持返回类型差异而不支持参数类型差异,那么所有这些都是没有意义的。但是几乎每个人都会认为这样一个半的实现会被完全破坏并且开始垃圾邮件bug报告,所以我不认为C团队会这么做。
我不知道这是否是目前C语言不支持它的官方原因,但它应该是一个很好的例子,说明它可能导致“只写”代码,C团队的设计理念之一是防止开发人员编写糟糕的代码。

让我重新表述一下这个问题:

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

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

< C++调用的这个特性是什么?

该特征称为“返回型协方差”。(正如另一个答案指出的,它也可能支持“形式参数类型逆变”,尽管C++没有)。 为什么C#中不支持它

正如我多次指出的,我们不必提供不支持某个特性的原因;所有功能的默认状态为“不受支持”。只有在投入大量时间和精力进行实现时,功能才会得到支持。相反,实现的特性必须有理由,考虑到制作它们的成本,这是非常好的理由

这就是说,有两大“反对”这一特性的观点是阻止它实现的主要因素

  • CLR不支持它。为了做到这一点,我们基本上必须实现精确匹配方法,然后生成一个调用它的助手方法。这是可行的,但会变得一团糟

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


  • 简言之,有时它肯定会很有用,这是一个经常需要的特性。然而,我们不太可能这样做。该功能的好处并不支付其成本;它使方法的语义分析变得相当复杂,我们没有真正简单的方法来实现它。

    如果“IClonable”接口是在代码之前编写的,克隆方法如何返回特定类型?我们唯一的保证是,克隆实现返回的任何内容都将从“对象”派生。由于您必须遵守确切的接口定义,因此必须返回接口所说的内容,即“Objec”
    abstract class Enclosure {}
    class Aquarium : Enclosure {}
    abstract class Animal 
    {
        public virtual Enclosure GetEnclosure();
    }
    
    class Fish : Animal
    {
        public override Aquarium GetEnclosure() { ...