【问题标题】:Is this expected C# 4.0 Tuple equality behavior?这是预期的 C# 4.0 元组相等行为吗?
【发布时间】:2010-12-01 18:26:49
【问题描述】:

我看到在 .NET 4.0 的两个新 Tuple 实例之间使用 .Equals 和 == 之间的不同行为。如果我在 Tuple 中的对象上覆盖了 Equals 并在 Tuple 上调用 .Equals ,则将调用 Equals 的覆盖。如果我在元组上使用 ==,则不会调用 Equals 的覆盖。这是设计使然吗?是否有意义?

编辑:从答案和 cmets 我可以看出我不清楚。我知道 Tuple 是一个引用类型,对于引用类型 == 将检查身份(ReferenceEquals)。但是,是否应该 Tuple 覆盖 == 来检查它包含的对象的相等性?为了一致性,可能不会。

例如,如果我有一个简单的对象

public class NameAndNumber
{
    public int Number { get; set; }
    public string Name { get; set; }

    public override bool Equals(object obj)
    {
        if (obj is NameAndNumber)
        {
            NameAndNumber other = (NameAndNumber)obj;
            return Number == other.Number && Name == other.Name;
        }

        return false;
    }
}

然后我做这样的事情:

Tuple<NameAndNumber, NameAndNumber> left = new Tuple<NameAndNumber, NameAndNumber>(
      new NameAndNumber { Name = "one", Number = 1 }, 
      new NameAndNumber { Name = "two", Number = 2 });
Tuple<NameAndNumber, NameAndNumber> right = new Tuple<NameAndNumber, NameAndNumber>(
      new NameAndNumber { Name = "one", Number = 1 }, 
      new NameAndNumber { Name = "two", Number = 2 });
bool operatorResult = left == right;
bool equalsResult = left.Equals(right);
Console.Out.WriteLine("operatorResult = {0}  equalsResult = {1}", 
        operatorResult, equalsResult);

我得到 operatorResult = false equalsResult = true

我应该期待吗?

我知道 NameAndNumber 上的 Equals 实现并不“正确”,它只是简化的示例代码。

我也尝试过实现 IEquatable、==、!= 和 GetHashCode。结果相同。

【问题讨论】:

  • 感谢您的回答和 cmets。我应该预料到这种行为。我正在用 .NET 4 实现替换我们自己编写的项目 3.5 Tuple 实现。我们的元组覆盖 == 以获得我在问题中预期的行为。因此,当它的行为与我们自定义的不完全一样时,我感到很惊讶。

标签: .net-4.0 c#-4.0 tuples


【解决方案1】:

您看到的结果来自design compromise,元组现在在 F# 和 C# 之间共享。重点是所有元组确实都是作为引用类型实现的,这不是很明显。

Tuples 是否应该进行深层或浅层相等性检查的决定已移至两个接口:IStructuralComparableIStructuralEquatable。请注意,这 2 个现在也由 Array 类实现。

【讨论】:

    【解决方案2】:

    对于引用类型:== 执行身份比较,即只有当两个引用都指向同一个对象时才会返回 true。虽然 Equals() 方法预计会执行值比较,即如果引用指向等效的对象,它将返回 true。

    对于 == 被重载的引用类型,它会比较两个引用是否引用同一个对象

    【讨论】:

    • 魔法字符串除外。所以我想我正在寻找更多的魔法。你会期望一个值类型的元组来做值检查吗?它没有。
    • 没有魔法——它只是重载了 ==/!= 运算符;您可以在反射器中将其视为 op_Equality 和 op_Inequality(在 System.String 上)。然而,,像int/float(在规范中定义)和Nullable&lt;T&gt;(使用“提升”运算符)之类的东西有魔力。
    • 我不确定我是否完全理解您的 cmets,您的示例代码中的 Tuple 是引用类型,不是吗。
    • @Marc Gravell。抱歉,魔术评论不清楚。我知道它是如何工作的,谢谢你的澄清。对 string 的覆盖使其行为与大多数其他 BCL 对象不同,这导致我有时认为 == 会神奇地适用于其他引用类型。这当然会导致我提出关于元组的问题,我应该自己弄清楚。
    • @J.W.对不起,我今天早上真的不清楚。我完全时差,不应该问问题。我真的想指出其他人所说的话。除非您覆盖它,否则 == 将进行身份比较。说魔法是一个糟糕的选择,增加了混乱。这并不意味着您的回答不适用于我指出的情况。谢谢。
    【解决方案3】:

    默认情况下,运算符 == 测试引用相等性,因此您看到的结果是预期的。

    Guidelines for Overriding Equals() and Operator == (C# Programming Guide):

    在 C# 中,有两种不同的类型 相等性:引用相等性(也 称为身份)和价值平等。 价值平等是普遍的 理解平等的含义:它 意味着两个对象包含 相同的值。例如,两个整数 值为 2 有值 平等。引用相等意味着 没有两个对象 比较。

    【讨论】:

      【解决方案4】:

      默认情况下,==(在一个类上)表示引用相等;即它们是同一个实例吗? object.ReferenceEquals(x,y) 会返回什么。

      您可以提供自己的 == / != 运算符来获得预期的行为 - 当您覆盖 Equals 时,覆盖 GetHashCode 也很重要(否则您会破坏使用作为键 - Why is it important to override GetHashCode when Equals method is overriden in C#?):

      public static bool operator == (NameAndNumber x, NameAndNumber y) {
          if (x == null && y == null) return true;
          if (x == null || y == null) return false;
          return x.Number == y.Number && x.Name == y.Name;
          // or if polymorphism is important: return x.Equals(y);
      }
      public static bool operator !=(NameAndNumber x, NameAndNumber y) {
          return !(x == y); // lazy but works
      }
      public override int GetHashCode() {
          return (Name == null ? 0 : Name.GetHashCode()) +
              17 * Number.GetHashCode();
      }
      

      【讨论】:

      • 我知道。正如我提到的,我展示了一个简化的实现,并将 GetHashCode 留给了一个较小的示例。但这不会改变元组的工作方式,我不能将它们添加到元组。我可以从 Tuple 派生并添加它们。
      • 如果多态是问题所在,那么让 == 使用 .Equals - 除此之外......这就是 == 的工作原理。如果你不超载它,它就不会这样工作!
      • @Marc Gravell。我想问题的核心在于 .net 4.0 中内置的 Tuple 类是否会以这种方式运行。它应该像标准引用类型一样工作还是将相等性检查委托给它包含的对象?它只是一个容器,所以我们应该关心两个容器是否相同或者应该 == 检查容器内容。这不是我对 Tuple 的实现,它是内置的 .net 4 之一。以前具有 Tuple 或 Pair 实现的第三方库将覆盖 == 检查所包含对象的相等性。
      • 啊,直到最近的编辑,这个问题并没有清楚地表明你在谈论 .NET 4.0s 的 Tuple&lt;...&gt; 类型。公平地说,我错过了标签;-p
      • @Marc Gravell。对于那个很抱歉。我试着让它更清楚,并学会了不要依赖标签。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-05-05
      • 1970-01-01
      • 1970-01-01
      • 2011-12-09
      • 1970-01-01
      • 1970-01-01
      • 2011-11-09
      相关资源
      最近更新 更多