这是预期的C#4.0元组相等行为吗?

时间:2009-09-27 07:51:04

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

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

编辑:从答案和评论我可以说我不清楚。我知道Tuple<>是引用类型,引用类型==将检查标识(ReferenceEquals)。但是,如果Tuple<> override ==检查它包含的对象是否相等?为了保持一致性,可能不是。

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

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 个答案:

答案 0 :(得分:14)

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

Tuples是应该进行深度还是浅度相等检查的决定已移至两个界面:IStructuralComparableIStructuralEquatable。请注意,这些2现在也由Array类实现。

答案 1 :(得分:6)

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

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

答案 2 :(得分:1)

默认情况下,运算符==测试参考相等性,因此您期望得到的结果。

请参阅Guidelines for Overriding Equals() and Operator == (C# Programming Guide)

  

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

答案 3 :(得分:1)

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

您可以提供自己的== /!=运算符来获得预期的行为 - 当您覆盖Equals时,重要的是覆盖GetHashCode(否则您将使用权作为关键点 - { {3}}):

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();
}