代码改变世界

C#中 Reference Equals, == , Equals的区别

2012-01-30 23:03  javaspring  阅读(253)  评论(0编辑  收藏  举报

1. ReferenceEquals, == , Equals
Equals , == , ReferenceEquals都可以用于判断两个对象的个体是不是相等。

a
) ReferenceEquals
ReferenceEquals是Object的静态方法,用于比较两个引用类型的对象是否是对于同一个对象的引用对于值类型它总是返回false。(因为Box以后的对象总是不同的,hehe)

b) ==是一个可以重载的二元操作符,可以用于比较两个对象是否相等。
对于内置值类型,==判断的是两个对象的代数值是否相等它会根据需要自动进行必要的类型转换并根据两个对象的值是否相等返回true或者false。例如:

Int a = 100;
Double b =100;

If(a == b)
Console.WriteLine(“equal supports compare between different types!”);

上面这段程序将会输出:
equal supports compare between different types!

而对于用户定义的值类型,如果没有重载==操作符,==将是不能够使用的。例如:


Struct Userstruct1;
Userstruct1 a;
Userstruct1 b;

If(a == b)
Console.WriteLine(“can == reach this far?”)

上面的这段代码是不能够通过编译的。可以通过重载使==作用于用户定义的值类型。

对于引用类型,== 默认的行为与ReferenceEquals的行为相同,仅有两个对象指向同一个Reference的时候才返回true。但是.NET Framework中的类很多对==进行了重载,例如String类的==与Equals的行为相同,判断两个字符串的内容是否相等。所以在应用中,对于 系统定义的引用类型建议不要使用==操作符,以免程序出现与预期不同的运行结果。

c) Equals 作为Object内置方法,Equals支持对于任意两个CTSCommon Type System对象的比较。
Equals它有静态方法和可重载的一个版本,下面的程序片断解释了这两个方法的用法,

int a = 5;
int b = 5;

If(Object.Equals(a ,b))
// you can also use if(a.Equals(b))
{
Console.WriteLine(“a is equal to b”);
}

事实上,这两个版本的结果完全相同,如果用户重载了Equals,调用的都是用户重载后的Equals。Equals的静态方法的好处是可以不必考虑用于比较的对象是否为null。

Equals方法对于值类型和引用类型的定义不同对于值类型,类型相同,并且数值相同(对于struct的每个成员都必须相同),则Equals返回 true,否则返回false。而对于引用类型,默认的行为与ReferenceEquals的行为相同,仅有两个对象指向同一个Reference的时 候才返回true。可以根据需要对Equals进行重载,例如String类的Equals用于判断两个字符串的内容是否相等。

 

//StringBuilder 类

//表示可变字符字符串。无法继承此类。


StringBuilder a = new StringBuilder();
a.Append("the test a");
String s1 = a.ToString();
String s2 = "the test a";

if (s2 == s1)
Console.WriteLine("== returns true");

if (Object.Equals(s2, s1))
{
Console.WriteLine("equals returns true");
}

if (Object.ReferenceEquals(s2, s1))
{
Console.WriteLine("ReferenceEquals returns true");
}

这个实例将输出:
== returns true
equals returns true

对于String类,直接声明s1 = “the test a”的话,输出结果将包含 "ReferenceEquals returns true",
因为默认的,String对于声明的相同的字符串在堆上只保留一个Copy,所以s1与s2将会指向相同的Reference

 

http://javasky.bloghome.cn/posts/142567.html

在讨论了运算符,并简要介绍了等于运算符后,就应考虑在处理类和结构的实例时相等意味着什么。理解对象相等比较的机制对编写逻辑表达式非常重要,另外,对实现运算符重载和数据类型转换也非常重要,本章的后面将讨论运算符重载。

    对象相等比较的机制对于引用类型(类的实例)的比较和值类型(基本数据类型,结构或枚举的实例)的比较来说是不同的。下面分别介绍引用类型和值类型的相等比较。

   5.3.1 引用类型的相等比较

    System.Object的一个初看上去令人惊讶的方面是它定义了3个不同的方法,来比较对象的相等性:ReferenceEquals()和Equals()的两个版本。再加上比较运算符==,实际上有4种进行相等比较的方式。这些方法有一些微妙的区别,下面就介绍这些方法。

================================================================

下面的最明了

1. ReferenceEquals()方法

    ReferenceEquals()是一个静态方法,测试两个引用是否指向类的同一个实例,即两个引用是否包含内存中的相同地址。作为静态方法,它不能重写,所以只能使用System.Object的实现代码。如果提供的两个引用指向同一个对象实例,ReferenceEquals()总是返回true,否则就返回false。但是它认为null等于null:

SomeClass x, y;

x = new SomeClass();

y = new SomeClass();

bool B1 = ReferenceEquals(null, null);                //return true

bool B2 = ReferenceEquals(null, x);                //return false

bool B3 = ReferenceEquals(x, y);                  //return false because x and y

                                            //point to different objects

2. 虚拟的Equals()方法

    Equals()虚拟版本的System.Object实现代码也比较引用。但因为这个方法是虚拟的,所以可以在自己的类中重写它,按值来比较对象。特别是如果希望类的实例用作字典中的键,就需要重写这个方法,以比较值。否则,根据重写Object.GetHashCode()的方式,包含对象的字典类要么不工作,要么工作的效率非常低。在重写Equals()方法时要注意,重写的代码不会抛出异常。这是因为如果抛出异常,字典类就会出问题,一些在内部调用这个方法的.NET基类也可能出问题。

3. 静态的Equals()方法

    Equals()的静态版本与其虚拟实例版本的作用相同,其区别是静态版本带有两个参数,并对它们进行相等比较。这个方法可以处理两个对象中有一个是null的情况,因此,如果一个对象可能是null,这个方法就可以抛出异常,提供了额外的保护。静态重载版本首先要检查它传送的引用是否为null。如果它们都是null,就返回true(因为null与null相等)。如果只有一个引用是null,就返回false。如果两个引用都指向某个对象,它就调用Equals()的虚拟实例版本。这表示在重写Equals()的实例版本时,其效果相当于也重写了静态版本。[Page]

4. 比较运算符==

最好将比较运算符看作是严格值比较和严格引用比较之间的中间选项。在大多数情况下,下面的代码:

bool b = (x == y);          //x, y object references

   表示比较引用。但是,如果把一些类看作值,其含义就会比较直观。在这些情况下,最好重写比较运算符,以执行值的比较。后面将讨论运算符的重载,但显然它的一个例子是System.String类,Microsoft重写了这个运算符,比较字符串的内容,而不是它们的引用。

5.3.2 值类型的相等比较

在进行值类型的相等比较时,采用与引用类型相同的规则:ReferenceEquals()用于比较引用,Equals()用于比较值,比较运算符可以看作是一个中间项。但最大的区别是值类型需要装箱,才能把它们转换为引用,才能对它们执行方法。另外,Microsoft已经在System.ValueType类中重载了实例方法Equals(),以便对值类型进行合适的相等测试。如果调用sA.Equals(sB),其中sA和sB是某个结构的实例,则根据sA和sB是否在其所有的字段中包含相同的值,而返回true或false。另一方面,在默认情况下,不能对自己的结构重载==运算符。在表达式中使用(sA==sB)会导致一个编译错误,除非在代码中为结构提供了==的重载版本。

另外,ReferenceEquals()在应用于值类型时,总是返回false,因为为了调用这个方法,值类型需要装箱到对象中。即使使用下面的代码:

bool b = ReferenceEquals(v, v);          //v is a variable of some value type

也会返回false,因为在转换每个参数时,v都会被单独装箱,这意味着会得到不同的引用。调用ReferenceEquals()来比较值类型实际上没有什么意义。

尽管System.ValueType提供的Equals()默认重载肯定足以应付绝大多数自定义的结构,但仍可以为自己的结构重写它,以提高性能。另外,如果值类型包含作为字段的引用类型,就需要重写Equals(),以便为这些字段提供合适的语义,因为Equals()的默认重写版本仅比较它们的地址。

http://www.zxbc.cn/html/20070809/25909.html

====================================================================================================================

http://msdn.microsoft.com/zh-cn/library/bsc2ak47(VS.80).aspx

Object.Equals 方法 (Object)

确定指定的 Object 是否等于当前的 Object

参数

obj

与当前的 Object 进行比较的 Object

返回值

如果指定的 Object 等于当前的 Object,则为 true;否则为false

Equals 的默认实现仅支持引用相等,但派生类可重写此方法以支持值相等。

对于引用类型,相等定义为对象相等;即这些引用是否引用同一对象。对于值类型,相等定义为按位相等。ValueType 类支持值类型。

给实现者的说明 此方法可由派生类重写。例如,如果两个对象表示相同的值,则许多基数据类型返回 true;否则返回false。 此方法仅比较基元和对象。若要比较更复杂的结构(如对象数组),必须重写该方法。 下面的语句对于Equals 方法的所有实现均必须为真。在列表中,x、y 和 z 表示不为 空引用(在 Visual Basic 中为 Nothing) 的对象引用。

  • 除涉及浮点型的情况外,x.Equals(x) 都返回 true。请参见 IEC 60559:1989, Binary Floating-point Arithmetic for Microprocessor Systems。

  • x.Equals(y) 返回与 y.Equals(x) 相同的值。

  • 如果 x 和 y 都为 NaN,则 x.Equals(y) 返回 true

  • 当且仅当 x.Equals(z) 返回 true 时,(x.Equals(y) && y.Equals(z)) 才返回 true

  • 只要不修改 x 和 y 引用的对象,对 x.Equals(y) 的相继调用将返回相同的值。

  • x.Equals(空引用(在 Visual Basic 中为 Nothing)) 返回 false

有关属于 Equals 方法的其他必需行为,请参见 GetHashCodeEquals 的实现必须不引发异常。 对于某些类型的对象,最好让Equals 测试值相等性而非引用相等性。如果两个对象具有相同的“值”,那么即使它们不是同一实例,这样的Equals 实现仍返回true。类型实施者决定对象的“值”的构成,但这通常是存储在对象的实例变量中的部分或全部数据。例如,String 的值基于字符串的字符;对于两个按同样的顺序包含完全相同的字符的字符串实例,String 类的Equals 方法返回 true。 实现 IComparable 的类型必须重写 Equals。 重写Equals 的类型也必须重写GetHashCode;否则,Hashtable 可能不正常工作。 如果编程语言支持运算符重载,并且您选择了重载给定类型的相等运算符,则该类型必须重写Equals 方法。这样的Equals 方法实现必须返回与相等运算符相同的结果。遵照此准则有助于确保使用Equals 的类库代码(如ArrayListHashtable)的行为方式与应用程序代码使用的相等运算符的方式一致。 下面是针对实现值类型的准则:
  • 考虑重写 Equals,以便在 ValueType 上获得比 Equals 的默认实现所提供的性能增强的性能。

  • 如果重写 Equals 并且语言支持运算符重载,则必须重载值类型的相等运算符。

下面是针对实现引用类型的准则:
  • 如果引用类型的语义是基于该类型表示某个(些)值的事实,则考虑对该类型重写 Equals

  • 即使大多数引用类型重写 Equals,它们也必须不能重载相等运算符。但是,如果实现的引用类型想要具有值语义(如复杂的数字类型),则必须重写相等运算符。

  • using System;

    public class Sample {
        void Method() {
        Object Obj1 = new Object();
        Object Obj2 = new Object();
        Console.WriteLine(Obj1.Equals(Obj2)); //===> false
        Obj2 = Obj1;
        Console.WriteLine(Obj1.Equals(Obj2)); //===> true
        }
    }

    ===================================================

    Object.Equals 方法 (Object, Object)

    确定指定的 Object 实例是否被视为相等。

    参数

    objA

    要比较的第一个 Object

    objB

    要比较的第二个 Object

    返回值

    如果 objA 是与 objB 相同的实例,或者如果两者均为空引用,或者如果objA.Equals(objB) 返回true,则为true;否则为 false

    Equals 的默认实现仅支持引用相等,但派生类可重写此方法以支持值相等。

    对于引用类型,相等定义为对象相等;即这些引用是否引用同一对象。对于值类型,相等定义为按位相等。ValueType 类支持值类型。

    在调用 objA.Equals(objB) 前,此方法首先检查两个参数是否均为空引用。

    下面的代码示例比较不同的对象。

    1. using System;  
    2.   
    3. public class MyClass {  
    4.    public static void Main() {  
    5.    string s1 = "Tom";  
    6.    string s2 = "Carol";  
    7.    Console.WriteLine("Object.Equals(/"{0}/", /"{1}/") => {2}",   
    8.       s1, s2, Object.Equals(s1, s2));  
    9.   
    10.    s1 = "Tom";  
    11.    s2 = "Tom";  
    12.    Console.WriteLine("Object.Equals(/"{0}/", /"{1}/") => {2}",   
    13.       s1, s2, Object.Equals(s1, s2));  
    14.   
    15.    s1 = null;  
    16.    s2 = "Tom";  
    17.    Console.WriteLine("Object.Equals(null, /"{1}/") => {2}",  
    18.        s1, s2, Object.Equals(s1, s2));  
    19.   
    20.    s1 = "Carol";  
    21.    s2 = null;  
    22.    Console.WriteLine("Object.Equals(/"{0}/", null) => {2}",   
    23.        s1, s2, Object.Equals(s1, s2));  
    24.   
    25.    s1 = null;  
    26.    s2 = null;  
    27.    Console.WriteLine("Object.Equals(null, null) => {2}",   
    28.        s1, s2, Object.Equals(s1, s2));  
    29.    }  
    30. }/* 
    31.  
    32. This code produces the following output. 
    33.  
    34. Object.Equals("Tom", "Carol") => False 
    35. Object.Equals("Tom", "Tom") => True 
    36. Object.Equals(null, "Tom") => False 
    37. Object.Equals("Carol", null) => False 
    38. Object.Equals(null, null) => True 
    39.  
    40. */  
  • Object.ReferenceEquals 方法

    确定指定的 Object 实例是否是相同的实例。


    参数


    objA

    要比较的第一个 Object


    objB

    要比较的第二个 Object



    返回值

  • 如果 objA 是与 objB 相同的实例,或者如果二者都为空引用,则为 true;否则为 false



    下面的代码示例使用 ReferenceEquals 确定两个对象是否是相同的实例。

    1. using System;  
    2.   
    3. class MyClass {  
    4.   
    5.    static void Main() {  
    6.       object o = null;  
    7.       object p = null;  
    8.       object q = new Object();  
    9.   
    10.       Console.WriteLine(Object.ReferenceEquals(o, p));  
    11.       p = q;  
    12.       Console.WriteLine(Object.ReferenceEquals(p, q));  
    13.       Console.WriteLine(Object.ReferenceEquals(o, p));  
    14.    }  
    15. }  
    16.   
    17. /* 
    18.  
    19. This code produces the following output. 
    20.  
    21. True 
    22. True 
    23. False 
    24.  
    25. */