gpt4 book ai didi

c# - IComparer 和 IEqualityComparer 接口(interface)中逆变的好处

转载 作者:太空狗 更新时间:2023-10-29 22:24:09 26 4
gpt4 key购买 nike

关于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从类型到类型映射 TF<T>T 中是逆变的如果每当UV是类型使得类型为 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/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com