代码改变世界

Effective C# 学习笔记(六)理解各种“等运算”操作

2011-07-03 22:50  小郝(Kaibo Hao)  阅读(459)  评论(0编辑  收藏  举报

首先明确数学意义上的相等三原则

  1. 自等性 a==a恒成立
  2. 对称性 a==b 那么b==a
  3. 传递性 a==b b==c 那么 a==c

 

而对象比较大体可分为 值类型和引用类型的比较

值类型相等的条件为两个值类型对象的类型和值内容一致(对于此种比较,最好重载其Equals方法,否则会很消耗资源,因为其在运行时执行时使用的是反射的方式获取对象的内容,然后再进行比较)

而引用类型对象相等的条件为两个引用类型对象的指向的同一个对象

 

C#中的等运算操作有如下6种方式进行比较

  1. public static bool ReferenceEquals (object left, object right);

其用来比较两个对象的指向的引用地址是否一致,只对引用类型对象起作用,若是值类型对象用其比较则永远返回false,即使是同一值类型对象也是如此。

示例代码:

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.");

 

  1. public static bool Equals (object left, object right);

其首先还是先用Object.ReferenceEquals()方法比较,若不同则看其中是否有一方为null,若是则返回false,若都不为空,则再调用比较对象自己的Equals方法。其实现方式大致如下:

public static new bool Equals(object left, object right)

{

// Check object identity

if (Object.ReferenceEquals(left, right) )

return true;

// both null references handled above

if (Object.ReferenceEquals(left, null) ||

Object.ReferenceEquals(right, null))

return false;

return left.Equals(right);

}

 

  1. public virtual bool Equals(object right);
  2. public static bool operator ==(MyClass left, MyClass right);

此种用法在值类型时,在运行时使用了反射机制获取两个比较对象等对象,并比较其中的值,所以需要重载以提高运行效率。所以在使用ValueType时,重载之。

  1. 重载Equals 方法的引用类型应实现IEquatable<T>范型接口(只在为了不使用原始的引用地址比较,而进行特殊需求比较时使用,使用时记得实现IEquatable<T>接口,实现Equals(T t)方法)

示例:

public class Foo : IEquatable<Foo>

{

public override bool Equals(object right)

{

// check null:

// this pointer is never null in C# methods.

if (object.ReferenceEquals(right, null))

return false;

if (object.ReferenceEquals(this, right))

return true;

 

// 先判断类型是是否相等

if (this.GetType() != right.GetType())

return false;

// 再判断内容是否相等

return this.Equals(right as Foo);

}

#region IEquatable<Foo> Members

public bool Equals(Foo other)

{

// elided.

return true;

}

#endregion

}

考虑到继承的原因,需提供对Iequatable<T>的实现,以提供Equals(T other)的重载,保证相等的“对称性”

如下示例所示:

public class B : IEquatable<B>

{

public override bool Equals(object right)

{

// check null:

if (object.ReferenceEquals(right, null))

return false;

// Check reference equality:

if (object.ReferenceEquals(this, right))

return true;

// Problems here, discussed below.

B rightAsB = right as B;

if (rightAsB == null)

return false;

return this.Equals(rightAsB);

}

#region IEquatable<B> Members

public bool Equals(B other)

{

// elided

return true;

}

#endregion

}

public class D : B, IEquatable<D>

{

// etc.

public override bool Equals(object right)

{

// check null:

if (object.ReferenceEquals(right, null))

return false;

if (object.ReferenceEquals(this, right))

return true;

// Problems here.

D rightAsD = right as D;

if (rightAsD == null)

return false;

if (base.Equals(rightAsD) == false)

return false;

return this.Equals(rightAsD);

}

#region IEquatable<D> Members

public bool Equals(D other)

{

// elided.

return true; // or false, based on test

}

#endregion

}

//Test:

B baseObject = new B();

D derivedObject = new D();

// Comparison 1. 子类可以转化为父类,其比较相等

if (baseObject.Equals(derivedObject))

  Console.WriteLine("Equals");

else

  Console.WriteLine("Not Equal");

// Comparison 2. 父类转化不成子类,不可相等

if (derivedObject.Equals(baseObject))

  Console.WriteLine("Equals");

else

  Console.WriteLine("Not Equal");

注意:只能在基类非System.ObjectSytem.ValueType的类中调用baseObject.Equals()方法,否则即与原来的默认实现相同,重载则没有意义了。

而且在引用类型重载Equals方法时,不要忘记重载GetHashCode()方法。

  1. 而值类型的类型若重载Equals方法,需要实现IStructuralEquality 接口

 

重载Equals方法的原则是:当要比较对象内容而不是对象引用是否相等时,进行重载操作。