- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
关于msdn page关于逆变,我发现了一个非常有趣的例子,它显示了“IComparer 中逆变的好处”
首先他们使用了一个相当奇怪的基类和派生类:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Employee : Person { }
我已经可以说这是一个糟糕的例子,因为没有任何类只是继承基类而不添加至少一点自己的东西。
然后他们创建了一个简单的 IEqualityComparer 类
class PersonComparer : IEqualityComparer<Person>
{
public bool Equals(Person x, Person y)
{
..
}
public int GetHashCode(Person person)
{
..
}
}
接下来是有问题的例子。
List<Employee> employees = new List<Employee> {
new Employee() {FirstName = "Michael", LastName = "Alexander"},
new Employee() {FirstName = "Jeff", LastName = "Price"}
};
IEnumerable<Employee> noduplicates =
employees.Distinct<Employee>(new PersonComparer());
现在我的问题 - 首先,在这种情况下,Employee 是一个不需要的类,它确实可以在这种情况下使用 PersonComparer,因为它实际上只是一个 person 类!
然而在现实世界中,Employee
将至少有一个新字段,比方说 JobTitle
。鉴于很明显,当我们想要不同的 Employees 时,我们需要考虑 JobTitle 字段进行比较,并且很明显,逆变比较器(例如 person 比较器)不适合该工作,因为它无法识别任何新成员员工已定义。
当然,任何语言特性,即使是非常奇怪的特性,都可以发挥作用,即使在某些情况下它不合逻辑,但在这种情况下,我认为它不会太频繁地成为默认行为。事实上,在我看来,我们有点破坏了类型安全,当一个方法需要一个 Employee 比较器时,我们实际上可以放入一个人或什至对象比较器,它会毫无问题地编译。虽然很难想象我们的默认场景是将 Employee 视为一个对象......或基本的 Person。
那么这些接口(interface)的默认值真的是一个很好的逆变吗?
编辑:我理解什么是逆变和协变。我在问为什么那些比较接口(interface)在默认情况下被更改为逆变。
最佳答案
逆变的定义如下。一张 map F
从类型到类型映射 T
至 F<T>
在 T
中是逆变的如果每当U
和 V
是类型使得类型为 U
的每个对象可以分配给类型为 V
的变量, 每个 F<V>
类型的对象可以分配给类型为 F<U>
的变量( F
反转分配兼容性)。
特别是,如果 T -> IComparer<T>
然后注意 IComparer<Derived>
类型的变量可以接收实现 IComparer<Base>
的对象.这是逆变。
我们这么说的原因IComparer<T>
在 T
中是逆变的是因为你可以说
class SomeAnimalComparer : IComparer<Animal> { // details elided }
然后:
IComparer<Cat> catComparer = new SomeAnimalComparer();
编辑:你说:
I understand what contravariance and covariance is. I am asking why those comparing interfaces were changed to be contravariant on default.
变了?我的意思是,IComparer<T>
是“自然地”逆变的。 IComparer<T>
的定义是:
public interface IComparer<T> {
int Compare(T x, T y);
}
请注意 T
仅出现在该界面的“in”位置。也就是说,没有返回 T
实例的方法。 . 任何这样的接口(interface)在T
中是“自然地”逆变的.
鉴于此,您有什么理由不想使其逆变?如果你有一个对象知道如何比较 U
的实例, 和 V
作业是否与 U
兼容,为什么你不应该也能够将此对象视为知道如何比较 V
的实例的东西? ?这就是逆变允许的。
在逆变之前你必须换行:
class ContravarianceWrapperForIComparer<U, V> : IComparer<V> where V : U {
private readonly IComparer<U> comparer;
public ContravarianceWrapperForIComparer(IComparer<U> comparer) {
this.comparer = comparer;
}
public int Compare(V x, V y) { return this.comparer.Compare(x, y); }
}
然后你可以说
class SomeUComparer : IComparer<U> { // details elided }
IComparer<U> someUComparer = new SomeUComparer();
IComparer<V> vComparer =
new ContravarianceWrapperForIComparer<U, V>(someUComparer);
逆变允许你跳过这些咒语,只说
IComparer<V> vComparer = someUComparer;
当然以上只是当V : U
.有了逆变,你可以在任何时候做 U
赋值与 V
兼容吗? .
关于c# - IComparer 和 IEqualityComparer 接口(interface)中逆变的好处,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6434207/
COW 不是奶牛,是 Copy-On-Write 的缩写,这是一种是复制但也不完全是复制的技术。 一般来说复制就是创建出完全相同的两份,两份是独立的: 但是,有的时候复制这件事没多大必要
我是一名优秀的程序员,十分优秀!