15

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

编辑:从答案和评论中,我可以说我不清楚。我知道 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。结果相同。

4

4 回答 4

14

您看到的结果来自设计妥协,元组现在在 F# 和 C# 之间共享。重点是所有元组确实都实现为引用类型,这并不那么明显。

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

于 2009-09-27T09:15:06.957 回答
6

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

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

于 2009-09-27T08:00:41.363 回答
1

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

请参阅覆盖 Equals() 和运算符 == 的指南(C# 编程指南)

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

于 2009-09-27T08:01:06.387 回答
1

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

您可以提供自己的 == / != 运算符来获得预期的行为 - 当您覆盖时,覆盖Equals也很重要GetHashCode(否则您会破坏使用作为键 -为什么在 C# 中覆盖 Equals 方法时覆盖 GetHashCode 很重要? ):

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();
}
于 2009-09-27T08:06:25.750 回答