Effective50.Rules06.理解几个等同性判断之间的关系.Note
当我们定义自己的类型时(无论是类还是Struct),应该为类型定义等同性的含义。C#定义了4中不同的函数来判断两个对象是否“相等”。
1.public static bool ReferenceEquals(object left,objec right)
2.public static bool Equals(object left,object right);
3.public virtual bool Equals(object right);
4.public static bool operator ==(MyClas left,MyClass right);
对于前两种静态函数,我们永远都应该重新定义。我们通常创建自己的实例的Equals(),来为类型定义等同的语义。偶尔需要覆盖operator==().
当然,这4种方法并不是等同性比较的唯一选择。覆盖Equals()方法的类型应该实现IEquatable<T>;实现值语义(value semantics)的类型同时还应该实现IStructurealEquality接口。这意味着有6种方法来表达等同性。
如果两个类型的变量指向的是同一个对象,我们认为他们是“引用相等”。如果两个值类型变量类型相同,而且包含同样的内容,是“值相等”。
Object.ReferenceEquals(leftObj,rightObj)
如果两个变量指向同一个对象,也就是他们拥有同样的对象标示(object identity),那么Object.ReferenceEquals(leftobj,rightobj)返回true;无论比较引用类型还是值类型,该方法判断依据都是对象的标示,而不是对象内容。
Object.ReferenceEquals(,)来判断值类型永远返回false,即使自己跟自己比较,其原因在于装箱,参见条目45.
第二,永远不要去重新定义静态函数Object.Equals().当你不知道两个变量运行时类型,可以使用该方法来判断两个对象是否相等。注意,任何类型都是System.Object的实例无论是值类型还是引用类型。
静态Object.Equals()把比较的具体操作委托给具体的类型来做,它的具体实现如下:
public static new bool Equals(Object left, Object right)
{
//Check object identity
if (object.ReferenceEquals(left, right))
return true;
if (object.ReferenceEquals(left, null) || object.ReferenceEquals(right, null))
return false;
return left.Equals(right);//最后将比较委托给具体类型
}
在阐述实例对象的Equals方法前,我们先谈谈等同性在数学方面的几个要点:
自反性(reflexive)无论a是什么类型,a==a应该返回true;
对称性(symmetric),意味着等同判断与判断的顺序无关,即,a==b与b==a应该是相同的结果。
传递性(transitive),a==b且b==c,那么a==c.
Object.Equals()实例函数默认实现与Object.ReferenceEquals()实现完全相同,都是根据对象标示。System.ValueType重写了实例Equals()方法,但是实现效率不高,使用反射,反射有许多缺点,特别是对性能要求比较高的地方,在程序中被频繁调用的功能。对值类型的建议非常简单,都要去覆盖ValueType.Equals()方法。
对于引用类型,当我们需要更改Equals()语义的时候才需要覆盖该方法。.NET中有许多类都是用值语义而不是引用语义,例如String对象。
自定义语义的Object.Equals()需要遵循定义的行为。当你覆盖Equals()时,也要同时为该类实现IEquatable<T>,下面是一个覆盖Syste.Object.Equals()实例方法标准实现模式