对象的同一性和等一性
开发人员经常需要比较对象。System.Object对象提供了名为Equals的虚方法。作用是对象包含两个相同值返回True。Object的Equals方法是向下面一样实现
public class Object
{
public virtual Boolean Equals(Object obj)
{
//如果两个引用指向一个对象他们肯定相同
if(this == obj) return true;
//假定对象不包含相同的值
return false;
}
}
这样实质只实现了同一性,而非等一性。Object的默认设置是不合理的。下面显示了Equals方法应该怎么正确的实现
1.如果obj实参为null返回false
2.如果obj和this引用同一个对象,返回true
3.如果obj和this引用不同类型返回false
4.针对类型定义的每个实例字段,将this字段中的值和obj字段进行比较,任何字节不相等返回false
5.调用基类的Equals方法来比较如果为false返回false
所以微软应该向下面这样实现Object的Equals方法
public class Object
{
public virtual Boolean Equals(Object obj)
{
//比较对象不能为null
if(obj == null) return false;
//要属于相同类型
if(this.GetType() != obj.GetType()) return false;
//所有字段匹配的前提下返回True
//由于Object没有任何字段,所以匹配
return true;
}
}
由于没有这样实现Equals,所以Equals的实现规则远远比想象的复杂。类型重写Equals时应该调用基类Equals实现。由于类型能重写Object的Equals方法,所以不能测试同一性。为了解决这个问题Object提供了静态方法ReferceEquals.
public class Object
{
public static Boolean ReferenceEquals(Object a, Object b)
{
return (a == b);
}
}
检测同一性务必使用ReferceEquals,不应使用==操作符(除非现将两个操作数转换为object),因为某个操作数可能重载了==操作符,为其赋值不同于同一性的语意。
System.Value内部重写了Object的Equals方法。并进行了正确的实现来执行相等性检测。ValueType的Equals内部是这样实现的
1.如果obj为null返回false
2.如果this和obj实参引用不同类型对象就返回false
3.针对类型定义的每个实例字段,都将this对象的值与obs对象的值进行比对。任何字段不相等返回false
4.返回true。ValueType的Equals方法不调用Object的Equals方法。
在内部ValueType的Equals方法利用了反射完成了步骤3.由于CLR反射机制慢,定义自己的类型时应该重写Equals方法来提供实现,提高性能。当然不调用base.Equals
重写Equals要符合相对性的四个特征
1.Equals必须自反
2.Equals必须对称
3.Equals必须可传递
4.Equals必须一致
重写Equals时还需要做下面几件事
让类型实现System.IEqualable<T>接口的Equal方法
重载==和!=操作运算符方法,在内部调用类型安全的Equals
另外如果是排序类型的比较类型应该实现ICoparable的ComoareTO方法和IComparabel<T>类型安全的CompareTo方法。如果实现了这些方法可以考虑重载各种比较运算符(<,>,<=,>=),在这些方法内部调用类型安全的ComparaTo方法
对象哈希码
FCL设计者认为,如果能将任何对象的任何实例放到哈希表集合中,能带来很多好处。System.Object提供了虚方法GetHashCoe,它能获得任意对象 的Int32哈希码。如果你重写了Equals方法,还应该重写GetHashCode方法。这是因为在Dictionary,Hashtable以及一些集合的实现中。要求两个对象必须具有相同的HashCode才视为相等。所以,重写Equal就必须重写gethashCode以确保相等性算法和对象哈希码算法一致。
public sealed class Point
{
private readonly int x,y;
public override int GetHashCode()
{
return x ^y;
}
}
选择算法计算哈希码请遵守以下规则
1.这个算法提供良好分布
2.可以在算法中调用基类的GetHashCode方法,并包含返回值
3.算法至少使用一个实例字段
4.理想情况下字段应该不可变
5.算法执行速度尽快
6.包含相同值的不同对象返回相同哈希码。