我看到在两个.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。相同的结果。
答案 0 :(得分:14)
您看到的结果来自design compromise,现在在F#和C#之间共享元组。重点是所有元组都确实被实现为引用类型,这并不是那么明显。
Tuples是应该进行深度还是浅度相等检查的决定已移至两个界面:IStructuralComparable
,IStructuralEquatable
。请注意,这些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();
}