【问题标题】:covariance/contravariance in C#C#中的协变/逆变
【发布时间】:2011-07-23 01:57:24
【问题描述】:

我有一个book,它解释了逆变/协方差如下:

  • 委托可以具有比其方法目标更具体的参数类型。这称为逆变
  • 委托的返回类型可能不如其目标方法的返回类型具体。这称为协方差

而且,这是一个例子。

using System;

delegate void StringAction(string s);
delegate object ObjectRetriever();

class Test
{
    static void Main()
    {
        StringAction sa = new StringAction(ActionObject);
        sa("hello");

        ObjectRetriever o = new ObjectRetriever(RetrieveString);
        object result = o();
        Console.WriteLine(result);
    }


    static string RetrieveString() {return "hello";}

    static void ActionObject(object o)
    {
        Console.WriteLine(o);
    }
}

我认为为了使用协方差/逆变,需要使用new,如示例中所示,但我似乎使用sa = ActionObjecto = RetrieveString 得到相同的结果。 (我用 Mono 测试过)。

  • 那为什么作者用new来解释协变/逆变呢?
  • 协方差/逆变思想背后的理论是什么?它只是描述object x = Everything inherit from object 的花哨名称吗?这个奇怪的名字是从哪里来的?它有什么用?

【问题讨论】:

标签: c# covariance contravariance


【解决方案1】:

我有一本书解释了逆变/协变如下......

这不是一个很好的方差解释。 究竟是什么被称为“协变”和“逆变”,这一点完全不清楚。

从未提及实际上是变体的东西。逆变的是从一个类型到具有该类型参数的委托的映射。逆变是映射关系的属性。

尝试阅读这篇文章,看看你是否理解得更好:

http://blogs.msdn.com/b/ericlippert/archive/2007/10/19/covariance-and-contravariance-in-c-part-three-member-group-conversion-variance.aspx

我认为为了使用协变/逆变,需要使用 new ,如示例中所示,但我似乎得到了相同的结果......

从 C# 2.0 开始,您可以说“d = M”或“d = new D(M)”——编译器只是将它们识别为编写同一事物的两种不同方式。

为什么作者使用 new 来解释协变/逆变?

我不知道。

协方差/逆变思想背后的理论是什么?

理论是,如果你有一个 ordering 关系——也就是说,如果说 X x = (Y)y 是合法的,那么 X 比 Y 大——并且你有一个 mapping,保留顺序关系,则映射是协变的。如果它反转排序关系,它是逆变

例如,假设 Animal 是比 Giraffe 更大的类型。因此,您可以将 Giraffe 类型的对象分配给 Animal 类型的变量。动物 > 长颈鹿。

现在进行从类型 T 到方法 M-that-takes-a-T 和委托类型 D-that-takes-a-T 的映射。

您可以将方法 M-that-takes-an-Animal 分配给 D-that-takes-a-Giraffe 类型的变量。 D(长颈鹿)> M(动物)但动物>长颈鹿。关系是颠倒的映射逆变的

它只是描述对象 x = 一切都继承自对象的花哨名称吗?

没有。它与该概念相关,因为对象是比几乎所有其他类型都更大的类型。但实际上的变体是保持或反转大小关系的映射

尝试阅读这篇文章,看看是否有帮助。

http://blogs.msdn.com/b/ericlippert/archive/2009/11/30/what-s-the-difference-between-covariance-and-assignment-compatibility.aspx

这个奇怪的名字是从哪里来的?

范畴论。

【讨论】:

  • 很遗憾,您的博客链接不再按预期工作。
【解决方案2】:

我见过的描述 C# 中协变/逆变的最佳信息是 Eric Lippert here 的一系列博客文章。请参阅从列表底部开始的十一部分系列。

有时有点难以阅读。但它解释了你一开始可以问的一切。 :)

它是在实际 C# 4.0 实现之前编写的,因此对语法的一些讨论已经过时,但其他一切似乎都按照描述的方式实现。

【讨论】:

  • +1 我刚刚阅读了他关于该主题的前几篇文章。
【解决方案3】:

这是一篇很棒的 wiki 文章:here

更多的是漏斗倾倒的方向以及将漏斗的宽度设置在适当的水平......

【讨论】:

    【解决方案4】:

    不知道你在第一个问题中的意思。

    第二个问题可以通过建议看演员来回答。逆变允许具有扩展类 B 的类 A 存储为类 B。

    逆变

    class A {
    }
    
    class B : public A {
    }
    
    B obj = new A();
    

    协方差允许类 B 扩展类 A 但存储为类 A。

    协方差

    class A {
    }
    
    class B : public A {
    }
    
    A obj = new B();
    

    【讨论】:

    • 你显然错了。协变/逆变处理泛型类型。
    • @Suroot : 我想问为什么作者在ObjectRetrieve o = RetrieveString 有效时使用ObjectRetriever o = new ObjectRetriever(RetrieveString);
    • 好吧,我可能太苛刻了。我的错,对不起。一般来说,方差与类型排序有关。在 C# 中,虽然它主要与泛型和委托相关联。继承类是非常微不足道的情况,我之前根本不认为它是协方差情况:)
    • @Ivan 其中一种实现是泛型的,但它指的是更宽 -> 窄,窄 -> 更宽数据类型的一般转换。我看到的主要地方是返回类型。注意:这不是唯一的情况,但(恕我直言)通常是使用最广泛的形式。
    • 伊万是正确的。 您所描述的绝对不是方差。赋值兼容性是与方差相关的排序关系,但变体是映射 保留或反转该关系。如果您想了解 assignment compatibilityvariance 之间的区别,这是我关于该主题的文章:blogs.msdn.com/b/ericlippert/archive/2009/11/30/…
    猜你喜欢
    • 2012-04-06
    • 1970-01-01
    • 2015-01-16
    • 1970-01-01
    • 2012-12-03
    • 2021-09-18
    相关资源
    最近更新 更多