再次讨论平等我偶然发现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 False
C2=C1 False
C2=C3 False
C3=C2 False
EqualityComparer<Class1>.Default.Equals
C1=C2 False
C2=C1 False
C2=C3 True BUG?
C3=C2 真正的BUG?
但是MSDN 文档建议:
给实现者的注意事项 如果您实现 Equals,您还应该重写 Object.Equals(Object) 和 GetHashCode 的基类实现,以便它们的行为与 IEquatable<T>.Equals 方法的行为一致。如果您确实重写了 Object.Equals(Object),那么在调用类上的静态 Equals(System.Object, System.Object) 方法时也会调用您重写的实现。此外,您应该重载 op_Equality 和 op_Inequality 运算符。这可确保所有相等性测试返回一致的结果,如示例所示。
从这一刻开始,我认为没有理由IEquatable<T>
为引用类型实现。谁能告诉我什么时候有任何意义?当我们以不同的方式看待类型(作为基本类型)时,我真的应该将不同的平等行为视为不一致吗?