System.Object类型提供了一个名为Equals的虚方法,它的作用是比较两个对象包含相同的值返回true。 System.Object是这样实现的:
public class Object{
public virtual Boolean Equals(Object obj)
{
if(this==obj) return ture;
return false;
}
}
假如this和obj实参引用同一个对象,就返回true,之所以合理是因为Equals知道一个对象肯定包含和它自身一样的值。假如obj实参引用不同对象的值,Equals就不能肯定对象是否包含和this相同的值,所以返回false。对于Object的Equals方法的默认实现来说,它实现的是 “同一性” identity。而非 “相等性” equality。 遗憾的是Object的Equals方法的默认实现看起来并不合理,思考应该如何正确重写Equals方法,下面展示了如何在内部实现一个Eqlaus方法的思路:
1.如果obj实参为null,返回false。
2.如果this和obj实参引用同一个对象,返回true,同一个对象的指针地址相同,在比较大对象时有助于提升性能。
3.如果this和obj实参引用不同类型的对象,返回false。int 和 string 你说能相同么。。
4.针对类型定义的每个实例字段,将this对象中的值与obj对象中的值进行比较,字段不相等就返回false。
5.调用基类的Equals方法,以便比较它定义的任何字段,如果基类的Equals方法返回false,就返回false,否者返回true。
所以有了如下的实现Object的Equals方法:
public class Object{
public virtual Boolean Equals(Object obj)
{
if(obj==null) return false;//要比较的对象不能为null
if(this.GetType()!=obj.GetType())return false;//如果对象类型不同,肯定不相等。
//if(this.value==obj.value)//在对象的字段都匹配下返回true。
return true;
}
}
因为一个类型可能重写了Object的Equals方法,所以不能在调用这个被重写的Equals方法来测试同一性,为了修正这个问题,Object提供了一个静态方法ReferenceEqualse,原型如下:
public class Object
{
public static Boolean ReferenceEquals(Object obj1,Object obj2)
{
return (obj1==obj2);
}
}
如果想看两个对象是否都指向同一个对象(同一性),就应该调用ReferenceEquals方法,不应该使用==操作符,因为其中的某个对象的类型可能重载了==操作符。
System.ValueType(所有的值类型都继承了它),重写了object的Equals方法,值类型的Equals方法正确的实现来执行值的相等性的检查(不是同一性)。ValueType是这样实现的:
1.如果obj实参为null,返回false。
2.如果this和obj实参引用不同的类型对象,返回false。
3.针对类型定义的每个实例字段,都将this对象中的值与obj对象中的值进行比较(通过调用Equals方法)。任何字段不等就返回false。
4.返回true,ValueType的Equals方法不会调用Object的Equals方法。
ValueType的Equals方法使用反射技术实现步骤3,因为反射机制比较慢,所以如果考虑性能,可以自己实现Equals方法,当然就不要掉用base.Equals了。
如果决定重写Equals,必须确定它符合相等性的4个特性。
1.Equals必须自反:x.Equals(x)肯定返回true。
2.Equals必须对称:x.Equals(y)为true,则y.equals(x)肯定为true。
3.Equals必须是可传递的:x.Equals(y)为true,y.equals(z)为true,则x.equals(z)肯定为true。
4.Equals必须是一致的:比较的两个值没变,Equals返回的值也不会变。
基本的数学知识啊啊。。。
假如处于排序的目的而比较类型的实例,那么类型还应实现System.IComparable的CompareTo方法和System.IComparable<T>的类型安全的CompareTo方法,如果实现了这些方法,还可以考虑重载各种比较操作符方法,并在内部调用类型安全的CompareTo方法。
如果你定义的一个类型重写了Equals方法,还应该重写GetHashCode方法。事实上如果重写了Equals方法没有重写GetHashCode方法,编译器就给一条警告“warning 重写Object.Equals(object o)但不重写Object.GethashCode()”
一个类型在定义了Equals后,之所以要同时定义GetHashCode是应为在一些集合的实现中,要求两个对象相等,必须具有相同的哈希码。所以重写GetHashCode方法是为了确保相等性算法和对象哈希码算法是一致的。
修改一个对象后,在这个对象的目标哈希桶里就找不到这个对象了。所以要修改哈希表中的键对象是,正确的做法是:移除原来的键值对,修改对象,把新的键值对添加到哈希表中。
System.ValueType实现的GethashCode也采用了反射机制,速度较慢。。
不要对哈希码持久化,因为未来的版本的Hash算法可能不同,生成的哈希码也就不同了!