none
关于==, Equals和ReferenceEquals的疑问 RRS feed

  • 问题

  • 在学习泛型引用比较时对==, Equals和ReferenceEquals的区别产生了疑惑,google了一下,发现网上的确有相关解答,但仅仅是针对未进行类型转换、装箱拆箱的解答,加上这些就有点晕了,各位高手能帮忙指点一下吗?尝试的代码及运行结果如下:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    
    namespace EqualTest
    {
        class Program
        {
            static void Main(string[] args)
            {
                #region unit 1
                string str1 = "Hello, world!";
                string str2 = "Hello, world!";
                Console.WriteLine("1");
                Console.WriteLine(str1.GetType() + "-" + str2.GetType());
                Console.WriteLine(str1 == str2);
                Console.WriteLine(Object.Equals(str1, str2));
                Console.WriteLine(Object.ReferenceEquals(str1, str2));
                #endregion
    
                #region unit 2
                Object obj1 = new Object();
                Object obj2 = new Object();
                obj1 = str1;
                obj2 = str2;
                Console.WriteLine("2");
                Console.WriteLine(obj1.GetType() + "-" + obj2.GetType());
                Console.WriteLine(obj1 == obj2);
                Console.WriteLine(Object.Equals(obj1, obj2));
                Console.WriteLine(Object.ReferenceEquals(obj1, obj2));
                #endregion
    
                #region unit 3
                string strAdd = " Good job!";
                str1 += strAdd;
                str2 += strAdd;
                Console.WriteLine("3");
                Console.WriteLine(str1.GetType() + "-" + str2.GetType());
                Console.WriteLine(str1 == str2);
                Console.WriteLine(Object.Equals(str1, str2));
                Console.WriteLine(Object.ReferenceEquals(str1, str2));
                #endregion
    
                #region unit 4
                obj1 = str1;
                obj2 = str2;
                Console.WriteLine("4");
                Console.WriteLine(obj1.GetType() + "-" + obj2.GetType());
                Console.WriteLine(obj1 == obj2);
                Console.WriteLine(Object.Equals(obj1, obj2));
                Console.WriteLine(Object.ReferenceEquals(obj1, obj2));
                #endregion
    
                #region unit 5
                int integer1 = 1;
                int integer2 = 1;
                Console.WriteLine("5");
                Console.WriteLine(integer1.GetType() + "-" + integer2.GetType());
                Console.WriteLine(integer1 == integer2);
                Console.WriteLine(Object.Equals(integer1, integer2));
                Console.WriteLine(Object.ReferenceEquals(integer1, integer2));
                #endregion
    
                #region unit 6
                Object objInt1 = new Object();
                Object objInt2 = new Object();
                objInt1 = integer1;
                objInt2 = integer2;
                Console.WriteLine("6");
                Console.WriteLine(objInt1.GetType() + "-" + objInt2.GetType());
                Console.WriteLine(objInt1 == objInt2);
                Console.WriteLine(Object.Equals(objInt1, objInt2));
                Console.WriteLine(Object.ReferenceEquals(objInt1, objInt2));
                #endregion
    
                Console.ReadLine();
            }
        }
    }
    

    以下有一点自己思考的+一些问题,请教以下大家以下这些问题,自己思考的答案可能也有些是错的,还望各位大师批评指正。

    unit1,可以理解,由于直接String进行比较,==和Equals返回True;由于两个String变量字符串相同,导致字符串留用,结果使用同一引用,ReferenceEquals返回True。

    unit2,由于对象的类型本身是无法改变的,类型转换成功本身仅是返回一个原始引用。对于object, 由于obj1和obj2引用相同,==、ReferenceEquals、Equals返回TRUE。

    unit3,String型与String型直接相比,==返回True可以理解;是否是因为CLR无法获知+=的变量内容,而无法判断是否能够做到字符串留用,以节省空间,只能在堆上分配一个新的空间吗?如果是,则可以解释ReferenceEquals返回的False,但Equals比较的是“确定指定的 Object 实例是否被视为相等”,为何又返回的是True呢?

    unit4,==可以理解,obj1和obj2由于是两个不用的引用,返回False;ReferenceEquals、Equals加上“对象的类型本身是无法改变的,类型转换成功本身仅是返回一个原始引用”的概念,是否同unit3问?

    unit5,INT型的直接==,可以理解;ReferenceEquals是由于INT型两次入栈+装箱,导致False;而执行Equals时同样也装箱了,equals比较的是“确定指定的 Object 实例是否被视为相等”,为何返回的是True呢?

    unit6,进行装箱,==返回False是由于非同一个引用,这样ReferenceEquals也返回False了;equals同unit5最后问?


    Become a .net Nanja

    2013年2月10日 13:14

答案

  • 你好!

    我的理解如下:
    ReferenceEquals是Object类的静态方法,对引用类型会直接比较是否指向同一个对象。对于值类型,由于会先进行装箱,所以永远不会相等。

    对预定义的类型而言,== 对于值类型对象直接比较对象的值;对于String类型以外的其他引用类型对象(预定义类型),会比较是否指向同一个对象;对于String类型,会比较它们的值。对于其他自定义类型重载的==操作符,会按照定义的逻辑来比较对象。

    Equals因为是Object类的虚方法,所以其结果是由调用运行时类型的Equals方法得出来的。

    参考:

    Virtual method

    http://msdn.microsoft.com/en-us/library/aa645767(v=vs.71).aspx


    Yoyo Jiang[MSFT]
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2013年2月12日 11:41
    版主
  • 楼上关于==和ReferenceEquals方法的解释很好。但是,我认为楼上没有理解LZ在Object.Equals上的问题。

    Yoyo Jiang混淆了bool object.Equals(object obj1, object obj2)(这是一个静态方法)和bool object.Equals(object obj2)(这是一个非静态的、虚拟的方法)。

    正确的解释:我想,这里用代码解释更加方便。

    先解释object类的bool object.Equals(object obj2)方法,它的定义可以视作如下这段代码的等价物:

    namespace System
    {
        public partial class Object
        {
            public virtual bool Equals(Object obj2)
            {
                return Object.ReferenceEquals(this, obj2);
            }
        }
    }

    而bool object.Equals(object obj1, object obj2)方法,可以视作如下代码的等价物:

    namespace System
    {
        public partial class Object
        {
            public static bool Equals(Object obj1, Object obj2)
            {
                if (ReferenceEquals(obj1, obj2))
                    return true;
                if (ReferenceEquals(obj1, null))
                    return false;
                return obj1.Equals(obj2);
            }
        }
    }

    它的逻辑:首先,引用了同一个对象,必定相等,否则,先确保第一个引用不是空(空引用不Equals任何一个非空的对象),然后调用第一个对象上的Equals方法。

    来看看这段代码:

    string str1 = "hello", str2 = "hello", str3;
    str3 = Console.ReadLine();
    str1 += str3;
    str2 += str3;
    Console.WriteLine(object.Equals(str1, str2));

    首先,如果没有编译器的优化,则str1和str2不引用同一个对象。按照object.Equals的规则,既然str1、str2都不是空引用而且引用了不同对象,则会调用str1.Equals(str2)这个东西,而str1的string重写(override)了Equals方法,所以实际上相当于str1.Equals(str2)。
    • 已标记为答案 Tony_Chan 2013年2月14日 0:44
    2013年2月13日 17:09

全部回复

  • 你好!

    我的理解如下:
    ReferenceEquals是Object类的静态方法,对引用类型会直接比较是否指向同一个对象。对于值类型,由于会先进行装箱,所以永远不会相等。

    对预定义的类型而言,== 对于值类型对象直接比较对象的值;对于String类型以外的其他引用类型对象(预定义类型),会比较是否指向同一个对象;对于String类型,会比较它们的值。对于其他自定义类型重载的==操作符,会按照定义的逻辑来比较对象。

    Equals因为是Object类的虚方法,所以其结果是由调用运行时类型的Equals方法得出来的。

    参考:

    Virtual method

    http://msdn.microsoft.com/en-us/library/aa645767(v=vs.71).aspx


    Yoyo Jiang[MSFT]
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2013年2月12日 11:41
    版主
  • 楼上关于==和ReferenceEquals方法的解释很好。但是,我认为楼上没有理解LZ在Object.Equals上的问题。

    Yoyo Jiang混淆了bool object.Equals(object obj1, object obj2)(这是一个静态方法)和bool object.Equals(object obj2)(这是一个非静态的、虚拟的方法)。

    正确的解释:我想,这里用代码解释更加方便。

    先解释object类的bool object.Equals(object obj2)方法,它的定义可以视作如下这段代码的等价物:

    namespace System
    {
        public partial class Object
        {
            public virtual bool Equals(Object obj2)
            {
                return Object.ReferenceEquals(this, obj2);
            }
        }
    }

    而bool object.Equals(object obj1, object obj2)方法,可以视作如下代码的等价物:

    namespace System
    {
        public partial class Object
        {
            public static bool Equals(Object obj1, Object obj2)
            {
                if (ReferenceEquals(obj1, obj2))
                    return true;
                if (ReferenceEquals(obj1, null))
                    return false;
                return obj1.Equals(obj2);
            }
        }
    }

    它的逻辑:首先,引用了同一个对象,必定相等,否则,先确保第一个引用不是空(空引用不Equals任何一个非空的对象),然后调用第一个对象上的Equals方法。

    来看看这段代码:

    string str1 = "hello", str2 = "hello", str3;
    str3 = Console.ReadLine();
    str1 += str3;
    str2 += str3;
    Console.WriteLine(object.Equals(str1, str2));

    首先,如果没有编译器的优化,则str1和str2不引用同一个对象。按照object.Equals的规则,既然str1、str2都不是空引用而且引用了不同对象,则会调用str1.Equals(str2)这个东西,而str1的string重写(override)了Equals方法,所以实际上相当于str1.Equals(str2)。
    • 已标记为答案 Tony_Chan 2013年2月14日 0:44
    2013年2月13日 17:09
  • 谢谢Gee Law的指正。

    关于Object.Equals(Object, Object) method, 也可以参照MSDN文档:

    Object.Equals Method (Object, Object)


    Yoyo Jiang[MSFT]
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    2013年2月14日 3:55
    版主