IComparer 和 IEqualityComparer 接口中逆变的好处

2024-03-07

On the msdn页面 http://msdn.microsoft.com/en-us/library/dd465120.aspx关于逆变,我发现一个非常有趣的例子,它显示了“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。鉴于很明显,当我们想要不同的员工时,我们需要考虑 JobTitle 字段进行比较,并且很明显,逆变比较器(例如人员比较器)不适合该工作,因为它无法知道任何新成员员工已定义。

当然,现在任何语言功能,即使是非常奇怪的功能,都可以有其用途,即使它在某些情况下不合逻辑,但在这种情况下,我认为它不会经常成为默认行为。事实上,在我看来,我们有点破坏了类型安全,当一个方法需要 Employee 比较器时,我们实际上可以放入一个人甚至对象比较器,并且它会毫无问题地编译。虽然很难想象我们的默认场景会将 Employee 视为一个对象……或基本的 Person。

那么对于这些​​接口来说,默认值真的是一个很好的逆变吗?

EDIT:我明白了什么是逆变和协变。我问为什么那些比较接口在默认情况下被更改为逆变。


逆变的定义如下。一张地图F从类型到类型的映射T to F<T>是逆变的T如果每当U and 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();

编辑:你说:

我明白了什么是逆变和协变。我问为什么那些比较接口在默认情况下被更改为逆变。

改变了?我是说,IComparer<T>是“自然”逆变的。的定义IComparer<T> is:

 public interface IComparer<T> {
     int Compare(T x, T y);
 }

注意T仅出现在该界面的“in”位置。也就是说,没有返回实例的方法T. Any这样的接口“自然”是逆变的T.

鉴于此,您有什么理由不想使其逆变呢?如果您有一个知道如何比较实例的对象U, and 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.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

IComparer 和 IEqualityComparer 接口中逆变的好处 的相关文章

随机推荐

  • 如何使用 Python 的 smtplib 发送电子邮件中的换行符?

    我编写了一个脚本 将消息写入文本文件并将其作为电子邮件发送 一切都很顺利 除了电子邮件最终似乎都在一行中 我添加换行符 n它适用于文本文件 但不适用于电子邮件 您知道可能的原因是什么吗 这是我的代码 import smtplib sys i
  • 为什么“‘数组’的存储大小不是恒定的”

    我是 C 语言新手 上周 我学习使用数组来解决问题 我发现当数组的数量太大时 比如1024 1024 它会显示错误 有些人建议我使用 static 来解决它 但是这个错误 标题 让我更加困惑 我想我已经定义了参数 它应该是不变的 但事实并非
  • Spring Kafka 和事务

    我想将 Spring Kafka 与事务一起使用 但我不太明白它应该如何配置以及它如何工作 这是我的配置 props put ProducerConfig ENABLE IDEMPOTENCE CONFIG true props put P
  • 用于解析 JSON 的 JavaScript eval() 替代方案

    快速提问 JavaScript 中的 Eval 不安全 不是吗 我有一个字符串形式的 JSON 对象 我需要将其转换为实际对象 以便获取数据 function PopulateSeriesFields result data eval re
  • 版本:属性的使用版本

    我认为 versions use releases不更新用于定义依赖项中版本的属性 是否也可以更新这些属性 我看到我们有一个versions update properties 但它的目的是将版本更新到最新版本 而不仅仅是用版本替换快照 一
  • Apple 的 TestFlight 应用程序是否会自动更新外部 Beta 测试人员安装的应用程序?

    我可以从 iTunes connect 控制面板看到测试版应用程序的每个版本有多少用户 TestFlight 何时更新这些应用程序 他们是否必须打开 TestFlight 并手动选择更新 当他们启动正在测试的应用程序时 它会自动发生吗 我似
  • Swift 内存管理是如何工作的?

    具体来说 Swift 内存管理如何使用委托模式与选项一起工作 由于习惯了用 Objective C 编写委托模式 我的本能是让委托weak 例如 在 Objective C 中 property weak id
  • 将尚无超链接的文本替换为超链接

    一个更好的例子是这样的 string That is a very nice ford mustang if only every other ford was quite as nice as this honda 我想用制造商和型号的链
  • Javascript 和 Jquery 中的 array.eq() 与 array[]

    访问数组时 什么时候适合使用 eq 函数 例如 我有 slides eq slidesLength 1 css z index slidesLength 1 后来我 for i 0 i
  • 这些 PMD 规则的原因是什么?

    数据流异常分析 已找到 DD 变量 变量 异常 行 n1 n2 数据流异常分析 已找到 DU 变量 变量 异常 行 n1 n2 DD 和 DU 听起来很熟悉 我想说的是与最弱的前后条件相关的测试和分析之类的事情 但我不记得具体细节了 Nul
  • spring boot jpa无法创建表

    当我设置时 我使用 spring boot starter data jpa 来持久化我的实体 spring jpa hibernate ddl auto update 发生错误 但值为create或create drop 一切顺利 创建了
  • 插入新项目时如何防止recyclerview自动滚动到底部?

    我在用RecyclerView查看我的数据 但是当有很多项目时 RecyclerView每次插入新项目时都会自动滚动到底部 如何预防呢 这是插入代码 Override public void onChildAdded NonNull Dat
  • “gcc -s”和“strip”命令有什么区别?

    我想知道这两者有什么区别 gcc s 从可执行文件中删除所有符号表和重定位信息 strip 丢弃目标文件中的符号 它们有相同的含义吗 您使用哪一个来 减少可执行文件的大小 加快其运行速度 gcc作为编译器 链接器 其 s选项是完成的事情链接
  • azure-arm-consumation:获取资源组的消耗

    在我的 Node js 项目中 我尝试使用azure arm 消费包 https learn microsoft com en us javascript api azure arm consumption view azure node
  • 为什么 ioctl 返回“错误地址”

    我使用下面的代码从嵌入式板的 SPI 端口输出数据 olimex imx233 micro 这不是板特定的问题 当我运行代码时ioctl返回 地址错误 我正在修改代码http twilight ponies cz spi test c ht
  • 如何获取由而不是插入触发器插入的表标识?

    我有一个问题描述如下 我有一张表 其中有一个而不是插入触发器 create table TMessage ID int identity 1 1 dscp varchar 50 GO Alter trigger tr tmessage on
  • 颤振变化波二次贝塞尔曲线到点

    通过下面的实现代码 我们有了这个自定义形状设计 在这个设计中我想将其更改为 当我刚刚学习创建自定义形状时 我无法做到这一点 有任何人可以帮助我如何实现这一目标吗 thanks import package flutter material
  • 需要新的委派权限时未征求用户同意

    我有一个 ASP NET 应用程序 它使用 OWIN 中间件对我的 Azure Active Directory 实例进行身份验证 在 Azure 门户中 我已为 ASP NET 应用程序创建了应用程序注册 并且身份验证效果很好 用户已经登
  • 如何使用 jsp:include param 标记将对象传递到另一个 jsp

    我正在尝试使用 jsp include 标记将 DTO 对象从一个 jsp 发送到另一个 jsp 但它始终将其视为字符串 我无法在包含的 jsp 文件中使用 DTO 这是一个代码
  • IComparer 和 IEqualityComparer 接口中逆变的好处

    On the msdn页面 http msdn microsoft com en us library dd465120 aspx关于逆变 我发现一个非常有趣的例子 它显示了 IComparer 中逆变的好处 首先 他们使用相当奇怪的基类和