zoukankan      html  css  js  c++  java
  • 对象判等

    一直都认为对于对象判等自己明白了,可是真当与别人深入交流时,茫然了~~~痛定思痛,花了足足三个小时,整理了一番。要想深入理解这块知识,必须对CLR内存管理机制有一定的了解。废话不多说。

    一、首先牢记两个基本概念:

    (1)值相等:表示两个对象的数据成员按内存位分别相等,即两个对象类型相等,并且具有相等和相同的字段。
    (2)引用相等:表示两个引用指向同一对象实例,也就是同一内存地址,因此可以由引用相等推出其值相等,反之则不然。

    二、本质分析:

    .NET对于对象判等总共有四个方法,虚拟的Equals()方法,静态的Equals()方法,静态的ReferenceEquals()方法,==操作符。现一一分析。

    (1)Equals()虚方法:用于比较两个类型实例是否相等,也就是判断两个对象是否具有相同的“值”代码实现可以“表示”为

    public virtual bool Equals(object obj)
    {
         return InernalEquals(this,obj);
    }
    //其中InternalEquals可以表示为
    if(this==obj)
    {
         return true;
    }
    else
    {
         return false;
    }

    可见默认情况下,Equals方法和referenceEquals方法是一样的,object的equals虚方法仅仅提供了最最简单的比较策略:如果两个引用指向同一对象,true,否则false。也就是判断是否引用相等。然而这种方法并未达到Equals比较两个对象值相等的目标,所以System.Object将这个任务交个其派生对象去重新实现,可以说Equals的比较结果取决于类的创建者是如何实现的,而非统一性约定。事实上,框架类中很多引用类型的Equals方法用于比较值相等,例如最典型的String类型对象是否相等,肯定关注起内容是否相等,判断的是值相等语义。

    (2)Equals()静态方法:实现了对两个对象的相等性判别,其在System.Object类型中实现过程可以"表示"为(而不是说其在Object类中就是这样实现的)

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

    所有Equals()静态方法的执行结果依次取决于三个条件
    ①是否为同一实例
    ②是否都为null
    ③第一个参数的Equals()实现

    故通常情况下Equals静态方法的执行结果常常受到“判等对象”的影响,如下测试。

    namespace 类型判等
    {
    class Program
    {
    static void Main(string[] args)
    {
    MyClassA classA
    = new MyClassA();
    MyClassB classB
    = new MyClassB();
    Console.WriteLine(Equals(classA,classB));
    //true,实际上执行的是classA.Equals(classB);返回true
    Console.WriteLine(Equals(classB, classA));//false,实际上执行的是classB.Equals(classA);返回false
    Console.ReadLine();
    }
    }
    class MyClassA
    {
    public override bool Equals(object obj)
    {
    return true;
    }
    }
    class MyClassB
    {
    public override bool Equals(object obj)
    {
    return false;
    }
    }
    }
    执行结果为:
    Image00011

    由执行结果知道,静态Equals方法的执行取决于==操作符,和Equals虚方法这两个因素,因此决议静态Equals方法的执行,就要在

    自定义类型中重写虚拟的Equals方法和重载==操作符。还有静态Equals方法可以解决两个值为null的对象的判等问题,而是用objA.Equals(objB)来判断两个null对象会抛出NullReferenceException异常。

    (3)静态ReferenceEquals()方法,因为ReferenceEquals为静态方法,所以不能重写该方法,只能使用System.Object中的实现代码,具体为
            public static bool ReferenceEquals(object objA, object objB)
            {
                return (objA == objB);
            }

    如下示例:

    namespace 类型判等
    {
    class Program
    {
    static void Main(string[] args)
    {
    MyClass classA
    = new MyClass();
    MyClass classB
    = new MyClass();
    //classA,classC指向同一对象实例
    MyClass classC = classA;

    Console.WriteLine(ReferenceEquals(classA,classB));
    //false
    Console.WriteLine(ReferenceEquals(classA, classC));//true
    Console.WriteLine(ReferenceEquals(null, null));//true
    Console.WriteLine(ReferenceEquals(classA, null));//false
    Console.ReadLine();
    }
    }
    class MyClass
    {
    }
    }

    结果:

    Image00012
    可见ReferenceEquals方法用于判断两个引用是否指向同一对象,也就强调的引用相等,因此ReferenceEquals比较同一类型的两个对象实例将范虎false而.NET认为null等于null。

    (4)“==”操作符

    值类型下:表示是否值相等,由值类型的根类System.ValueType提供了实现,
    引用类型下:表示是否“引用相等”即两个引用指向同一个对象实例。牢记此话。

    当然也有例外 还是string ==表示的是值相等,而非引用相等。

    三、实际应用

    值类型判等

    ①Equals,System.Valuetype重载了System.Object的Equals方法,用于实现对实例数据的判等。

    ②ReferenceEquals 永远返回false说明:用ReferenceEquals()方法比较两个值类型变量毫无意义,结果肯定是false,即使是2个值相等的变量,因为在ReferenceEquals()方法比较前,被比较的值类型变量将会被装箱操作,隐性地创建两个不同对象,所以其地址引用也将不同。

    ③== 为重载的==的值类型,将比较两个值是否“按位”相等(不懂,只知道是比较两个值是否相等,还请高人指点!)

    引用类型判等

    ①静态的ReferenceEquals:两个实例对象是否指向同一引用地址。

    ②静态的Equals:
         是否为同一实例
         是否都为null
         第一个参数的Equals()实现

    ③虚拟的Equals默认为引用地址比较。

    ④== 默认为引用地址比较

    四、小结——重写Equals()方法 1、经过对四种不同类型判等方法的讨论,我发现不管是Equals静态方法,Equals虚拟方法,==操作符的执行结果,都可能受到重写Equals方法的影响,所以在对象判等时就必须注意自定义类型中如何实现Equals方法,以及实现怎么样的Equals方法,不同的类型,“相等”会有偏差。

    2、因此Equals方法的执行结果往往取决于自定义类型的具体实现规则,而为什么.NET提供这种机制

    (1)对象判等取决于需求,没必要为所有.NET类型完成逻辑判等,System.Object也无法满足各种需求的判等方法。

    (2)不同类型的判等的处理不同,通过多态机制在派生类中处理各自的判等实现是明智的。

    3、重写Equals要综合考虑值类型,引用类型的判等,同时要兼顾父类所带来的影响,另外遵循三个原则,自发,传递,对称,还要注意重写GetHashCode()方法。

    另外关于对象判等还得注意String类型的字符串驻留机制导致的String判等的特殊性。

    题外话:本贴主要参考园子里王涛老师写的那本《你必须知道的.NET》一书,推荐大家都去看看此书,绝对值得一看。另有不当之处,还望各位指正。

  • 相关阅读:
    HDU 2553 N皇后问题
    HDU 1251 统计难题(Trie tree)
    NYOJ 325 zb的生日
    dedecms文章页调用tag关键词_增加内链和关键字密度
    用DEDECMS做手机网站
    DedeCMS模板文件结构
    DEDECMS如何让栏目外部链接在新窗口中打开
    dedecms arclist中的自增变量 autoindex的说明
    dedecms 分页样式
    dedecms 修改默认html存放目录
  • 原文地址:https://www.cnblogs.com/liujb/p/2069978.html
Copyright © 2011-2022 走看看