【发布时间】:2013-12-11 18:11:20
【问题描述】:
再次讨论平等时,我偶然发现了EqualityComparer<T>.Default.Equals()。我更喜欢为引用类型调用此方法,而不是 object.Equals()。
现在我觉得我大错特错了。
object.Equals() 使用可覆盖的实例 Equals() 方法提供正确的多态行为,而 EqualityComparer<T>.Default.Equals() 调用 IEquatable<T>.Equals() 如果已实现。
现在考虑这个小程序:
public class Class1 : IEquatable<Class1>
{
public int Prop1 { get; set; }
public bool Equals(Class1 other)
{
if (other == null)
return false;
return Prop1 == other.Prop1;
}
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
return Equals(obj as Class1);
}
}
public class Class2 : Class1, IEquatable<Class2>
{
public int Prop1 { get; set; }
public int Prop2 { get; set; }
public bool Equals(Class2 other)
{
if (other == null)
return false;
return Prop1 == other.Prop1 && Prop2 == other.Prop2;
}
public override bool Equals(object obj)
{
if (obj == null || GetType() != obj.GetType())
{
return false;
}
return Equals(obj as Class2);
}
}
class Program
{
static void Main(string[] args)
{
var c1 = new Class1 {Prop1 = 10};
var c2 = new Class2 {Prop1 = 10, Prop2 = 5};
var c3 = new Class2 {Prop1 = 10, Prop2 = 15};
Console.WriteLine("Object.Equals()");
Console.WriteLine("C1=C2 {0}",Equals(c1,c2));
Console.WriteLine("C2=C1 {0}",Equals(c2, c1));
Console.WriteLine("C2=C3 {0}",Equals(c2, c3));
Console.WriteLine("C3=C2 {0}", Equals(c3, c2));
var dec1 = EqualityComparer<Class1>.Default;
Console.WriteLine();
Console.WriteLine("EqualityComparer<Class1>.Default.Equals");
Console.WriteLine("C1=C2 {0}", dec1.Equals(c1, c2));
Console.WriteLine("C2=C1 {0}", dec1.Equals(c2, c1));
Console.WriteLine("C2=C3 {0} BUG?", dec1.Equals(c2, c3));
Console.WriteLine("C3=C2 {0} BUG?", dec1.Equals(c3, c2));
Console.ReadKey();
}
}
它显示了在相等语义中带来不一致是多么容易:
Object.Equals()
C1=C2 错误
C2=C1 错误
C2=C3 错误
C3=C2 错误
EqualityComparer.Default.Equals
C1=C2 错误
C2=C1 错误
C2=C3 真正的BUG?
C3=C2 真正的BUG?
不过MSDN Documentation 建议:
给实现者的注意事项 如果你实现了 Equals,你还应该 覆盖 Object.Equals(Object) 的基类实现和 GetHashCode 使它们的行为与 IEquatable
.Equals 方法。如果您确实覆盖了 Object.Equals(Object), 在对静态的调用中也会调用您覆盖的实现 类上的 Equals(System.Object, System.Object) 方法。在 此外,您应该重载 op_Equality 和 op_Inequality 运营商。这确保了所有相等性测试返回一致 结果,示例说明了。
从这一刻开始,我认为没有理由为引用类型实现IEquatable<T>。
谁能告诉我什么时候有任何意义?
当我们以不同的方式(作为基本类型)看待类型时,我真的应该将不同的相等行为视为不一致吗?
【问题讨论】:
-
就我个人而言,我所做的是使用
object.Equals()覆盖调用IEquatable<T>.Equals()。除了蓬松的“设计简洁”之外,没有任何技术原因——简单的对象不能与任何其他对象“等同”,这就是为什么实现一个指定你可以与谁进行比较的接口对我来说是有意义的。 -
@我会在最后一次索赔中打电话给 BS。你看过那个方法的实现了吗?
-
你没有让
object.Equals(object)等同于IEquatable<Class1>.Equals(Class1)中的Class1就像你应该拥有的那样(如果你将它与Class2进行比较,它们并不总是具有相同的结果),所以你会看到令人困惑的结果。更常见的等同方式是说对象必须具有完全相同的类型才能被视为相等(如您的Equals(object)方法) -
这只是
Class1中IEquatable<T>.Equals的不良实现,将实现虚拟化并在Class2中覆盖它 -
@Will "If the two objects do not represent the same object reference and neither is null, it calls objA.Equals(objB) and returns the result." 除非某个对象有可能以某种方式与自身引用相等但与自身不相等,否则最终将返回与调用
x.Equals(y)相同的结果会的。
标签: c# .net equality iequalitycomparer iequatable