zoukankan      html  css  js  c++  java
  • .NET对象判等归纳与总结

    1、引言

      最近在看《CLR via C#》看到对象判等的那一节,觉得这也是.NET基础知识中比较重要的部分就写一篇博文来简单的总结归纳一下。

    2、.NET下的对象判等

      在.NET中关于对象判等有四个方法。依次是:System.Object.ReferenceEquals、System.Object.Equals、Instance.Equals(实例方法)、Operator==操作符。下面我们就来比较下这四个方法的异同点。

      我们打开ILSpy反编译软件,看一下System.Object基类中关于对象判等的方法。如图所示:

      2.1 System.ReferenceEquals方法

      我们看到System.Object.ReferenceEquals方法比较的是两个引用对象的内存地址。即:如果两个引用类型的指针都指向堆上面的同一个对象,那么返回True,否则返回False。注意:对于两个值类型调用该方法会永远返回False,因为值类型在转化为Object类型的时候需要进行装箱(在堆上分配对象),两个值类型的对象会在堆上面分配两个对象。所以永远返回False

      2.2 Operator ==操作符

      ==操作符在.NET中根据值类型和引用类型的的不同会有不同的默认行为。当比较的双方是引用类型时,==操作符默认比较的是两个对象的内存地址。即与System.Object.Refernece方法一致。当比较的双方是值类型(编译器内置的基元类型时)是比较值是否相等。

      2.3 System.Object.Equals方法

      通过上面的源码我们看到这个方法主要依赖于==操作符和Equals的实例方法。如果objA,objB两个对象指向同一个内存对象那么就返回True。否则的话比较结果就会依赖于Instance.Equals方法。

      2.4 Instance.Equals方法

      我们通过上面的源码分析,可以看到return RuntimeHelpers.Equals(this, obj);这一句代码。其实在内部System.Object提供的这个Equals实例方法的逻辑很简单。就是比较obj==this。显然这个对于我们自定义的类型比较是不合理的。这个方法是virtual的,我们可以重写这个方法来自定义我们的比较规则。下面来看一下具体的重写规则。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Cryptography;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace CryptUntility
    {
        class BaseClass
        {
            public int baseFieldInt;
            public string baseFiledString;
            private bool flag;
    
            public BaseClass(int filedInt, string fieldString,bool flag)
            {
                this.baseFieldInt = filedInt;
                this.baseFiledString = fieldString;
                this.flag = flag;
            }
    
            public override bool Equals(object obj)
            {
                //如果obj为null肯定不相等,因为调用该方法的this不可能是null,否则会出现异常
                if (obj == null)
                {
                    return false;
                }
                //如果引用的是同一个对象,肯定相等
                if (ReferenceEquals(this, obj))
                {
                    return true;
                }
                //是否能够转型到DevideClass,如果不能说明不是同一类型的,肯定不相等
                BaseClass baseClass = obj as BaseClass;
                if (baseClass == null)
                {
                    return false;
                }
                //接下来依次比较各个值是否相等
                if (FiledEqule(this, baseClass))
                {
                    return true;
                }
                return false;
            }
    
            private bool FiledEqule(BaseClass obj1, BaseClass obj2)
            {
                if (obj1.baseFieldInt == obj2.baseFieldInt
                    && obj1.baseFiledString.Equals(obj2.baseFiledString)
                    && obj1.flag == obj2.flag)
                {
                    return true;
                }
                return false;
            }
        }
    
        class DevideClass : BaseClass
        {
            public int devideFidldInt;
            public string devideFieldString;
    
            public DevideClass(int filedInt, string fieldString, bool flag)
                : base(filedInt, fieldString, flag)
            {
                this.devideFidldInt = filedInt;
                this.devideFieldString = fieldString;
            }
    
            public override string ToString()
            {
                return String.Format("{0},{1}", devideFidldInt.ToString(), devideFieldString);
            }
    
            /// <summary>
            /// 重写比较规则
            /// </summary>
            /// <param name="obj"></param>
            /// <returns></returns>
            public override bool Equals(object obj)
            {
                //如果obj为null肯定不相等,因为调用该方法的this不可能是null,否则会出现异常
                if (obj == null)
                {
                    return false;
                }
                //如果引用的是同一个对象,肯定相等
                if(ReferenceEquals(this,obj))
                {
                    return true;
                }
                //是否能够转型到DevideClass,如果不能说明不是同一类型的,肯定不相等
                DevideClass devideObj = obj as DevideClass;
                if (devideObj == null)
                {
                    return false;
                }
                //接下来依次比较各个值是否相等
                if (FiledEqule(this, devideObj))
                {
                    /**
                     * 如果当前类是从System.Object类继承的那么不需要调用base方法
                     * 否则需要调用base(基类方法)来比较基类型的字段
                     * 因为有些基类字段有可能是私有的,需要将其提交到基类进行比较
                     * */
                    return base.Equals(devideObj);
                }
                return false;
            }
    
    
            private bool FiledEqule(DevideClass obj1, DevideClass obj2)
            {
                if (obj1.devideFidldInt == obj2.devideFidldInt
                    && obj1.devideFieldString.Equals(obj2.devideFieldString)
                    && obj1.baseFieldInt == obj2.baseFieldInt
                    && obj1.baseFiledString.Equals(obj2.baseFiledString))
                {
    
                    return true;
                }
                return false;
            }
        }
    
        class Program
        {
            static void Main(string[] args)
            {
                DevideClass dev1 = new DevideClass(120, "GG", true);
                DevideClass dev2 = new DevideClass(120, "GG", false);
    
                Console.WriteLine("dev1==dev2:"+dev1.Equals(dev2));
    
                Console.Read();
            }
        }
    }
    

      在以上的代码中我们看到我们在重写的时候具体的流程如下:

      1、第一步判断参数obj是否为null。作为实例方法那么this对象本身肯定不能是null,否则的话爆出异常的。如果obj==null那么肯定不相等。

      2、使用ReferenceEquals方法来判断两个对象是否指向同一个内存对象。这是加快对象判断的一个方法。

      3、接下来我们可以尝试着对需要比较的对象进行转型,如果不能转型成功,那么与this对象不是同一类型的对象。类型不同肯定不会相等。

      4、接下来我们可以依次比较我们自定义对象的各个字段,使用系统内置的Equals来进行比较。注意:如果一个类型的上一级基类不是System.Object类型,那么在进行字段比较的时候需要调用基类的方法进行比较。因为基类中可能有些private字段是需要通过基类的Equals方法来来进行验证的。

    3、最后的注意点

      我们重新Equals方法的同时也必须重新GetHashCode方法。相关内容我有机会会写出了和大家分享。大家也可以网上查阅相关的知识点。

  • 相关阅读:
    WampServer Mysql配置
    Java实现 蓝桥杯VIP 算法提高 陶陶摘苹果2
    Java实现 蓝桥杯VIP 算法提高 陶陶摘苹果2
    Java实现 蓝桥杯VIP 算法提高 陶陶摘苹果2
    Java实现 蓝桥杯VIP 算法提高 质因数2
    Java实现 蓝桥杯VIP 算法提高 质因数2
    Java实现 蓝桥杯VIP 算法提高 质因数2
    Java实现 蓝桥杯VIP 算法提高 质因数2
    Java实现 蓝桥杯VIP 算法提高 质因数2
    Java实现 蓝桥杯VIP 算法提高 前10名
  • 原文地址:https://www.cnblogs.com/dreamGong/p/4588265.html
Copyright © 2011-2022 走看看