【问题标题】:Fast Equals by ReferenceEquals for deeply immutable C# 9 records?ReferenceEquals 的快速 Equals 用于深度不可变的 C# 9 记录?
【发布时间】:2021-05-14 02:08:30
【问题描述】:

当被检查的对象具有相同的引用时,是否有可能告诉 C# 编译器一个深度不可变的记录类型 (C# 9+) 可以通过立即返回 true 来缩短其 Equals 检查?

对于“深度不可变记录”,我的意思是所有属性在任何深度都是不可变的:

public record MyImmutableRecord {
   public int MyProp { get; init; }
   public IReadOnlyList<AlsoImmutable> MyList { get; init; }
}

编译器会自动创建一个检查所有属性的Equals 方法。来自 ILSpy:

public virtual bool Equals(MyImmutableRecord? other)
{
   return (object)other != null
     && EqualityContract == other!.EqualityContract
     && EqualityComparer<int>.Default.Equals(MyProp, other!.MyProp)
     && ...
}

首先检查参考可以大大加快速度。当相等时,我们肯定知道内容也相等。如果没有,我们必须继续检查属性:

public virtual bool Equals(MyImmutableRecord? other) {
   if (object.ReferenceEquals(this, other)
     return true;
   return ... // Like above
}

有没有机会告诉编译器我们有一个真正不可变的对象并使用这个技巧?或者有人知道这是否是为未来计划的吗?否则我想我将不得不为每个不可变记录编写自己的 Equals 实现?

【问题讨论】:

  • 我不确定您认为“真正不可变”会给这个聚会带来什么。如果你有两个对同一个对象的引用,为什么可变性会影响相等?例如。如果它是同一个对象,那么它包含的任何引用都只会存在一次,因此当然会指向同一个被引用的对象。
  • 在我的示例中,如果我使用List 而不是IReadOnlyList,我可以从外部更改列表的内容。主对象的引用将保持不变,但其内容发生了变化。这就是为什么我的提案需要“真正”或“深度”不变性。
  • 但是......如果它仍然是相同的参考,那么......好吧,它们是相同的。您对 Equals 的建议修改是检查记录的引用相等性,仅此而已。可变性仍然不起作用。除非你想问一个与你实际问的问题不同的问题,否则就是
  • @Andi 对于 csharplang github 来说这是一个更好的问题
  • 顺便说一句,请注意您的记录可能不是真正不可变的。在某些情况下,狡猾的开发人员可以将列表转换为具体的 List 类型并修改集合

标签: c# c#-9.0 c#-record-type


【解决方案1】:

答案很简单(感谢 cmets)。当然,当它们的引用相等时,两个对象总是相等......独立于它们的可变性。可能我刚刚想到了这个想法,因为不可变数据结构鼓励开发人员在整个代码中重用和共享对象,这比可变类所做的要多得多。

问题应该是:

为什么自动生成的Equals 方法在详细检查成员之前不包含对引用相等性的检查?

我在 csharplang 项目中问这个:https://github.com/dotnet/csharplang/discussions/4411

编辑 2021-02-11:这现在是 Visual Studio 16.10 的路线图:https://github.com/dotnet/roslyn/issues/51136

【讨论】:

  • 也在c#design meeting中讨论过,使其成为规范的正式部分
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-12
  • 2012-02-25
  • 2016-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-01-10
相关资源
最近更新 更多