又是弱智问题
==和equal得看具体实现的方法是比较什么 一般自定义的类型 得自己复写该方法
ReferenceEqual是比较是否同一个引用
谢谢
Most languages do provide a default implementation of the equality operator (==) for reference types. Therefore, you should use care when implementing == on reference types. Most reference types, even those that implement the Equals method, should not override ==.
所以,算是个教训,没事不要重载 类 的==操作符……Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=601363
C# 语言规范 |
预定义的引用类型相等运算符为:
bool operator==
(object x, object y); bool operator!=
(object x, object y);
这些运算符返回两个引用是相等还是不相等的比较结果。
由于预定义的引用类型相等运算符接受 object
类型的操作数,因此它们适用于所有那些没有为自己声明适用的 operator
==
和 operator
!=
成员的类型。相反,任何适用的用户定义的相等运算符都有效地隐藏上述预定义的引用类型相等运算符。
预定义引用类型相等运算符要求操作数是引用类型值或 null
值;此外,它们还要求存在从一种操作数类型到另一种操作数类型的标准隐式转换(第 6.3.1 节)。除非这两个条件都为真,否则将发生编译时错误。这些规则中值得注意的含义是:
A
和 B
,并且 A
和 B
都不从对方派生,则两个操作数不可能引用同一对象。因此,此运算被认为是编译时错误。
对于 x
==
y
或 x
!=
y
形式的运算,如果存在任何适用的 operator
==
或 operator
!=
,运算符重载决策(第 7.2.4 节)规则将选择该运算符而不是上述的预定义的引用类型相等运算符。不过,始终可以通过将一个或两个操作数显式强制转换为 object
类型来选择预定义的引用类型相等运算符。示例
using System; class Test { static void Main() { string s = "Test"; string t = string.Copy(s); Console.WriteLine(s == t); Console.WriteLine((object)s == t); Console.WriteLine(s == (object)t); Console.WriteLine((object)s == (object)t); } }
产生输出
True False False False
变量 s
和 t
引用两个包含相同字符的不同 string
实例。第一个比较输出 True
,这是因为是在两个操作数都为 string
类型时选定预定义的字符串相等运算符(第 7.9.7 节)。其余的比较全都输出 False
,这是因为是在一个或两个操作数为 object
类型时选定预定义的引用类型相等运算符。
注意,以上技术对值类型没有意义。示例
class Test { static void Main() { int i = 123; int j = 123; System.Console.WriteLine((object)i == (object)j); } }
输出 False
,这是因为强制转换创建对已装箱 int
值的两个单独实例的引用。
以前一直没有关心这个问题,被别人问起,特记录一下. ( From csdn)
在讨论了运算符,并简要介绍了等于运算符后,就应考虑在处理类和结构的实例时相等意味着什么。理解对象相等比较的机制对编写逻辑表达式非常重要,另外,对实现运算符重载和数据类型转换也非常重要,本章的后面将讨论运算符重载。
对象相等比较的机制对于引用类型(类的实例)的比较和值类型(基本数据类型,结构或枚举的实例)的比较来说是不同的。下面分别介绍引用类型和值类型的相等比较。
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()的实例版本时,其效果相当于也重写了静态版本。
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()的默认重写版本仅比较它们的地址。