明白ReferenceEquals(), static Equals(), instance Equals(), 和运算行 符==之间的关系。
当你创建你自己的类型时(不管是类还是结构),你要 定义类型在什么情况下是相等的。C#提供了4个不同的方法来断定两个对象是否 是相等的:
public static bool ReferenceEquals
( object left, object right );
public static bool Equals
( object left, object right );
public virtual bool Equals( object right);
public static bool operator==( MyClass left, MyClass right );
这种语言让你可以为上面所有的4种方法创建自己的版 本。But just because you can doesn't mean that you should.你或许从 来不用重新定义前面两个方法。你经常遇到的是创建你自己实例的Equals()方法 ,来为你的类型定义语义;或者你偶而重载运==运算符,但这只是为了考虑值类 型的性能。幸运的是,这4个方法的关系,当你改变其中一个时,会影响到其它 的几个。是的,须要4个方法来完整的测试对象是否完全相等。但你不用担心, 你可以简单的搞定它们。
和C#里其它大多数复杂元素一样,这个(对相等 的比较运算)也遵守这样的一个事实:C#充许你同时创建值类型和引用类型。两 个引用类型的变量在引用同一个对象时,它们是相等的,就像引用到对象的ID一 样。两个值类型的变量在它们的类型和内容都是相同时,它们应该是相等的。这 就是为什么相等测试要这么多方法了。
我们先从两个你可能从来不会修 改的方法开始。Object.ReferenceEquals()在两个变量引用到同一个对象时返回 true,也就是两个变量具有相同的对象ID。不管比较的类型是引用类型还是值类 型的,这个方法总是检测对象ID,而不是对象内容。是的,这就是说当你测试两 个值类型是否相等时,ReferenceEquals()总会返回false,即使你是比较同一个 值类型对象,它也会返回false。这里有两个装箱,会在原则16中讨论。(译注: 因为参数要求两个引用对象,所以用两个值类型来调用该方法,会先使两个参数 都装箱,这样一来,两个引用 对象自然就不相等了。)
int i = 5;
int j = 5;
if ( Object.ReferenceEquals( i, j ))
Console.WriteLine( "Never happens." );
else
Console.WriteLine( "Always happens." );
if ( Object.ReferenceEquals( i, i ))
Console.WriteLine( "Never happens." );
else
Console.WriteLine( "Always happens." );
你或许决不会重新定义 Object.ReferenceEquals(),这是因为它已经确实实现了它自己的功能:检测两 个变量的对象ID(是否相同)。
第二个可能从来不会重新定义的方法是静 态的Object.Equals()。这个方法在你不清楚两个参数的运行类型时什么时,检 测它们是否相等。记住:C#里System.Object是一切内容的最终基类。任何时候 你在比较两个变量时,它们都是System.Object的实例。因此,在不知道它们的 类型时,而等式的改变又是依懒于类型的,这个方法是怎样来比较两个变量是否 相等的呢?答案很简单:这个方法把比较的职责委交给了其中一个正在比较的类 型。静态的Object.Equals()方法是像下面这样实现的:
public static bool Equals( object left, object right )
{
// Check object identity
if (left == right )
return true;
// both null references handled above
if ((left == null) || (right == null))
return false;
return left.Equals (right);
}