.NET类型对象的判等(Equals)

    技术2022-05-11  81

    申明::本文内容来自<<.NET框架程序设计>>    .NET类型分为:基元类型(Primitive type),引用类型(Reference type),值类型(Value type),所有这些类型的祖先是System.Object,System.Object提供了四个成员函数:Equals GetHashCode GetType ToString ,以确保每个继承类型都有一个最小的操作集合,但是在我们设计.NET类型的时候,这些成员的实现不能满足我们的需要,比如System.Object.Equals的默认实现就只是对两个比较的对象进行引用判等(确定指定的 Object 实例是否是相同的实例),即ReferenceEquals,所以,为我们自己的类型提供这些操作的重写是必须的,这样才能确保我们的类型如我们的期待运行,下面对重写Equals进行讨论.

    1.引用类型<1>类型(这里指我们自己设计的类型)直接派生自System.Object或者类型的所有基类均没有提供了非System.Object.Equals的实现

    public class MyObject : object{  public int m_id;  public string m_name;

      public MyObject(){}

      public override bool Equals(object obj)  {    if(obj==null)      return false;    if(obj.GetType()!=this.GetType())      return false;    MyObject other = (MyObject)obj;

        //比较值类型字段,使用值类型本身的Equals方法    if(!m_id.Equals(other.m_id))      return false;

        //比较引用类型字段,使用Object.Equals方法    if(!Object.Equals(this.m_name,other.m_name))      return false;

        return true;  }    public static bool operator==(MyObject o1,MyObject o2){   return Object.Equals(o1,o2);  }

      public static bool operator!=(MyObject o1,MyObject o2){   return !Object.Equals(o1,o2);  } }

    看看Object.Equals(object objA,object objB)的实现,上面对引用类型字段的比较和==操作符号的重载用到了它,他的实现就是如果指向同一实体为真,任意为空为假,接下来才调用比较类型本身的判等方法,这样的效率显然更高。

    public static bool Equals(object objA,object objB){  if(objA==objB) reurn true;  if((objA==null)||(objB==null)) return false;  return objA.Equals(objB);}

    <2>类型的基类提供了非System.Object.Equals的实现public class MyObjectEx: MyObject{  public int m_idEx;  public string m_nameEx;  public MyObjectEx(){ }     public override bool Equals(object obj)  {    if(!base.Equals(obj))      return false;

        if(obj==null)      return false;    if(obj.GetType()!=this.GetType())      return false;    MyObjectEx other = (MyObjectEx)obj;

        if(!m_idEx.Equals(other.m_idEx))      return false;    if(!Object.Equals(this.m_nameEx,other.m_nameEx))      return false;

        return true;  }

      public static bool operator==(MyObjectEx o1,MyObjectEx o2){   return Object.Equals(o1,o2);  }

      public static bool operator!=(MyObjectEx o1,MyObjectEx o2){   return !Object.Equals(o1,o2);  }}

    这里不同的只是多了

    if(!base.Equals(obj))      return false;

    很好理解,调用基类型的Equals实现,比较基类字段是否相等,如果这都不等,类型就不可能相等了.

    2.值类型的Equals重写

    首先要知道值类型都派生自System.ValueType,而System.ValueType又是派生自System.Object,我们先看看ValueType的Equals实现public class ValueType{  public override bool Equals(object obj)  {    if(obj==null)      return false;    Type thisType = this.GetType();    if(thisType!=obj.GetType())      return false;    FieldInfo[] fields = thisType.GetFields(BindingFlags.Public |                   BindingFlags.NonPublic | BindingFlags.Instance);    for(int i=0; i<fields.Length; i++)    {      object thisValue = fields[i].GetValue(this);      object thatValue = fields[i].GetValue(obj);      if(!Object.Equals(thisValue,thatValue))        return false;    }    return true;  }}

    这里用到了反射类System.Reflection,用来获取每个进行比较的类型的所有字段信息,以便对每个字段值进行比较.下面继续我们的值类型Equals方法重写struct MyValueType : ValueType {  public string m_refType;  public int m_valType;  public override bool Equals(object obj){    if(!(obj is MyValueType))      return false;    return this.Equals((ValueType)obj);  }

      public bool Equals(MyValueType obj){    if(!Object.Equals(this.m_refType,obj.m_refType))      return false;    if(!this.m_valType.Equals(obj.m_valType))      return false;    return true;  }

      public static bool operator==(MyValueType v1,MyValueType v2)  {    return v1.Equals(v2);  }

      public static bool operator!=(MyValueType v1,MyValueType v2){    return !v1.Equals(v2);  }}这里,对我们的值类型的Equals进行了重写,原书作者是说“尽管ValueType的Equals实现已经提供了一个很好的操作,并且适合绝大多数类型,但是我们仍然提供自己的Equals实现,原因是我们的实现效率更高,并且可以避免很多装箱操作”然而书的译者却否定了这个说法,他说不应该提供这样的重写,原因是这个重写有危险,传入的参数类型可能是一个可以隐式转化为MyValueType的类型,如:

    if(myvaluetype.Equals(theothertype))

    这样在调用的时候,首先theothertype进行的隐式转化为MyValueType,这就把theothertype的实际类型改了,因为对theothertype的隐式转化得到的肯定是MyValueType类型,这样调用的就是我们的public bool Equals(MyValueType obj),即使类型不同,字段值相同,也会得到true,这不是我们所期望的,所以这点还是佩服译者,考虑的深刻,能发现大师的错误。所以,对于值类型的Equals,我们还是不对其进行重写,就使用值类型基类ValueType提供的Equals,进行判等,它已经能满足我们的要求!但是如果确实对程序性能要求很严格,不妨我们对上面的做点修改:

    public override bool Equals(object obj){  if(!(obj is MyValueType))    return false;  MyValueType other = (MyValueType)obj;                  if(!Object.Equals(this.m_refType,other.m_refType))    return false;  if(!this.m_valType.Equals(other.m_valType))    return false;  return true;}

    这样减少了使用反射,但也不能避免调用的时候对参数的装箱操作,不过也稍微提高了一些性能,也如译者所提及的,不管是引用类型还是值类型,不要提供强类型版本的Equals判等.


    最新回复(0)