为什么"Covariance"和"Contravariance"的概念在实现接口方法时适用?

Ami*_*mit 7 .net c# covariance contravariance

用例是这样的:

public class SomeClass : ICloneable
{
    // Some Code

    // Implementing interface method
    public object Clone()
    {
        // Some Clonning Code
    }
}
Run Code Online (Sandbox Code Playgroud)

现在我的问题是,如果我们考虑协方差和反演的Funda,为什么不能使用"SomeClass(因为它是从对象派生的)"作为Clone()方法的返回类型

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

Eri*_*ert 9

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

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

abstract class Enclosure {}
class Aquarium : Enclosure {}
abstract class Animal 
{
    public virtual Enclosure GetEnclosure();
}
Run Code Online (Sandbox Code Playgroud)

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

class Fish : Animal
{
    public override Aquarium GetEnclosure() { ... 
Run Code Online (Sandbox Code Playgroud)

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

该功能称为"返回类型协方差".(正如另一个答案指出的那样,也可以支持"形式参数类型逆转",尽管C++没有.)

为什么C#不支持它?

正如我多次指出的那样,我们不必提供不支持功能的原因; 所有功能的默认状态为"不支持".只有当大量的时间和精力投入到实现功能得到支持时.相反,该功能实现必须考虑要花多少钱让他们为他们的理由,而且是不错的理由.

也就是说,有两个重要的"反对"这个功能是防止它完成的主要因素.

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

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

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

  • 所以明显的后续问题是:*"为什么Anders认为返回类型协方差是一个不好的特征?"* (3认同)

Aar*_*ght 6

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

例如:

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) { ... }
}
Run Code Online (Sandbox Code Playgroud)

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

public class QuestionableFoo : IFoo
{
    public double Flurp(Array array) { ... }
    public object Flurp(IEnumerable enumerable) { ... }
}
Run Code Online (Sandbox Code Playgroud)

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

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

public class EvilFoo : IFoo
{
    public object Flurp(ICollection collection) { ... }
    public object Flurp(ICloneable cloneable) { ... }
}
Run Code Online (Sandbox Code Playgroud)

哪一个获奖?这是一个完全有效的过载,但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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

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