编程语言的协方差和违反
-
18-09-2019 - |
题
谁能解释我,《编程语言理论》中协方差和违反的概念?
解决方案
协方差 从某些收藏班的角度来看很简单,最好的思考 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
不会错, , 正确的?
到现在为止,您可能想知道所有这些与仿制药有何关系。答案是,方差也可以应用于仿制药。前面使用的示例 object
和 string
数组。在这里,代码使用通用列表而不是数组:
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#编译器将产生错误。