谁能解释我,《编程语言理论》中协方差和违反的概念?

有帮助吗?

解决方案

协方差 从某些收藏班的角度来看很简单,最好的思考 List. 。我们可以 参数化List 与某种类型参数的类 T. 。也就是说,我们的列表包含类型的元素 T 对于一些 T. 。列表将是协变量的

s是t iff列表的子类型[s]是列表的子类型[t

(我在哪里使用数学定义 iff 意思是 当且只有.)

这是一个 List[Apple] 是一个 List[Fruit]. 。如果有一些例程接受 List[Fruit] 作为一个参数,我有一个 List[Apple], ,然后我可以将其作为有效参数传递。

def something(l: List[Fruit]) {
    l.add(new Pear())
}

如果我们的收集课 List 是可变的,那么协方差就没有意义,因为我们可能认为我们的例行程序可以添加其他一些水果(不是苹果),如上所述。因此,我们只能喜欢 不变 收集课程是协变的!

其他提示

这是我有关如何在C#4.0添加新方差功能的文章。从底部开始。

http://blogs.msdn.com/ericlippert/archive/tags/covariance+and+contravariance/default.aspx

在之间有一个区别 协方差逆向.
非常粗略的是,如果操作保留类型的顺序,则是协变量的,并且如果 逆转 这个命令。

订购本身旨在将更多的类型表示为比更特定类型更大的类型。
这是C#支持协方差的一个例子。首先,这是一系列对象:

object[] objects=new object[3];
objects[0]=new object();
objects[1]="Just a string";
objects[2]=10;

当然可以将不同的值插入数组中,因为它们最终都来自 System.Object 在.NET框架中。换句话说, System.Object 是一个非常一般的或 大的 类型。现在,这是一个支持协方差的地方:
将较小类型的值分配给较大类型的变量

string[] strings=new string[] { "one", "two", "three" };
objects=strings;

变量对象,该对象类型 object[], ,可以存储实际上类型的值 string[].

想一想 - 到了一点,这就是您所期望的,但这不是。毕竟, string 源自于 object, string[] 才不是 从获得 object[]. 。在此示例中,对协方差的语言支持无论如何都可以使分配成为可能,在许多情况下,您会发现这一点。 方差 是使语言更直观地发挥作用的功能。

这些主题的考虑非常复杂。例如,基于前面的代码,以下是两种情况,会导致错误。

// Runtime exception here - the array is still of type string[],
// ints can't be inserted
objects[2]=10;

// Compiler error here - covariance support in this scenario only
// covers reference types, and int is a value type
int[] ints=new int[] { 1, 2, 3 };
objects=ints;

违反功能的一个例子更为复杂。想象这两个类:

public partial class Person: IPerson {
    public Person() {
    }
}

public partial class Woman: Person {
    public Woman() {
    }
}

Woman 是从 Person, , 明显地。现在认为您有这两个功能:

static void WorkWithPerson(Person person) {
}

static void WorkWithWoman(Woman woman) {
}

其中一个功能可以用一个(什么都没关系) Woman, ,另一个更笼统,可以与从 Person. 。在 Woman 事物的一面,您现在也有这些:

delegate void AcceptWomanDelegate(Woman person);

static void DoWork(Woman woman, AcceptWomanDelegate acceptWoman) {
    acceptWoman(woman);
}

DoWork 是一个可以接受的函数 Woman 并引用一个也采用的函数 Woman, ,然后通过 Woman 代表。考虑一下 多态性 您这里有的元素。 Person更大Woman, , 和 WorkWithPerson更大WorkWithWoman. WorkWithPerson 也考虑了 更大AcceptWomanDelegate 出于差异的目的。

最后,您有这三行代码:

Woman woman=new Woman();
DoWork(woman, WorkWithWoman);
DoWork(woman, WorkWithPerson);

一个 Woman 实例是创建的。然后叫陶克,传递 Woman 实例以及对 WorkWithWoman 方法。后者显然与代表类型兼容 AcceptWomanDelegate - 类型的一个参数 Woman, ,无返回类型。不过,第三行有点奇怪。方法 WorkWithPerson 服用 Person 作为参数,不是 Woman, ,按照要求 AcceptWomanDelegate. 。尽管如此, WorkWithPerson 与代表类型兼容。 逆向 使它成为可能,因此在委托较大的情况下 WorkWithPerson 可以存储在较小类型的变量中 AcceptWomanDelegate. 。再次是直观的事情: 如果 WorkWithPerson 可以与任何 Person, ,通过 Woman 不会错, , 正确的?

到现在为止,您可能想知道所有这些与仿制药有何关系。答案是,方差也可以应用于仿制药。前面使用的示例 objectstring 数组。在这里,代码使用通用列表而不是数组:

List<object> objectList=new List<object>();
List<string> stringList=new List<string>();
objectList=stringList;

如果您尝试此操作,您会发现这不是C#中受支持的方案。在C#版本4.0以及.NET Framework 4.0中,已清理了通用中的差异支持,现在可以使用新的关键字 出去 带有通用类型参数。他们可以为特定类型参数定义和限制数据流的方向,从而使方差起作用。但是在 List<T>, ,类型的数据 T 两个方向流动 - 类型上有方法 List<T> 那回来 T 值以及其他接收此类价值的人。

这些定向限制的重点是 在有意义的地方允许差异, ,但要 防止问题 就像以前的数组示例之一中提到的运行时错误一样。当类型参数正确装饰时 或者 出去, ,编译器可以检查并允许或禁止其差异 编译时间. 。微软已努力将这些关键字添加到.NET框架中的许多标准接口,例如 IEnumerable<T>:

public interface IEnumerable<out T>: IEnumerable {
    // ...
}

对于此界面,类型的数据流 T 对象很清楚: 它们只能从该界面支持的方法中检索,而不会传递到它们. 。结果,可以构建类似于类似的示例 List<T> 尝试先前描述,但使用 IEnumerable<T> :

IEnumerable<object> objectSequence=new List<object>();
IEnumerable<string> stringSequence=new List<string>();
objectSequence=stringSequence;

自第4.0版以来,此代码是C#编译器可以接受的 IEnumerable<T> 由于 出去 类型参数的指定符 T.

在使用通用类型时,重要的是要了解差异以及编译器应用各种欺骗性的方式,以使您的代码工作按照您的期望。

关于差异的知识比本章所涵盖的要多更多,但是这足以使所有进一步的代码都可以理解。

参考:

Bart de Smet有一个关于协方差和违反的博客条目 这里.

C#和CLR都允许将方法与委托人结合时,允许参考类型的协方差和矛盾变化。协方差意味着方法可以返回从代表的返回类型派生的类型。相互变化意味着方法可以采用作为代表参数类型的基础的参数。例如,给定这样定义的委托:

委托对象mycallback(fileStream s);

可以构建该委托类型的实例,绑定到原型的方法

像这样:

字符串somemethod(流S);

在这里,SomeMethod的返回类型(String)是从代表的返回类型(Object)派生的类型;允许这种协方差。 SomeMethod的参数类型(流)是一种类型,是代表参数类型(FileStream)的基类;允许这种反对变异。

请注意,仅支持参考类型,而不是为价值类型或void支持协方差和逆转变化。因此,例如,我无法将以下方法绑定到mycallback委托:

int32一些hotershod(流S);

即使某些hothermethod的返回类型(INT32)是从mycallback的返回类型(对象)派生的,但由于INT32是值类型,因此不允许这种协方差形式。

显然,不能将价值类型和void用于协方差和相互变化的原因是因为这些事物的内存结构有所不同,而参考类型的内存结构始终是指针。幸运的是,如果您尝试执行不支持的事情,则C#编译器将产生错误。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top