学习笔记---Equal()方法、ReferenceEqual()方法以及运算符==的区别
看到这个问题是在做一道java考试题时看到的, 我当时认为这个java题目的答案在C#中也是成立的。说来也奇怪,不知道从何时起养成的习惯,学东西总觉得还是再确认一下比较好,结果一确认问题一大堆……。网上搜了些文章看了看,还是有点模糊,最终MSDN是个好东西呀,什么时候一出问题想到的是MSDN而不是谷歌或百度,我估计我就又成长一步了,啰嗦这么多,结论——MDSN很重要
ReferenceEquals()和Equals()方法在MSDN中的解释:
C# 中有两种不同的相等:引用相等和值相等。值相等是大家普遍理解的意义上的相等:它意味着两个对象包含相同的值。例如,两个值为 2 的整数具有值相等性。引用相等意味着要比较的不是两个对象,而是两个对象引用,且两者引用的是同一个对象。这可以通过简单的赋值来实现,如下面的示例所示:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
System.Object a = new System.Object();
System.Object b = a;
System.Object.ReferenceEquals(a, b); //returns true
//这里稍作更改,以证明Equals和ReferenceEquals的结果:
Console.WriteLine(System.Object.ReferenceEquals(a, b).ToString()); //returns true
System.Object c = new System.Object();
System.Object d = new System.Object();
c = (object)100;
d = (object)100;
Console.WriteLine(System.Object.Equals(c,d).ToString()); //returns true
Console.WriteLine(System.Object.ReferenceEquals(c,d).ToString()); //returns false
在上面的代码中,只存在一个对象,但存在对该对象的多个引用:a 和 b。由于它们引用的是同一个对象,因此具有引用相等性。如果两个对象具有引用相等性,则它们也具有值相等性,但是值相等性不能保证引用相等性 (修改代码以证明) 。若要检查引用相等性,应使用 ReferenceEquals。若要检查值相等性,请使用 Equals。
Equals()方法在MSDN中的参考:
Equals 的默认实现仅支持引用相等,但派生类可重写此方法以支持值相等。
对于引用类型,相等定义为对象相等,即这些引用是否引用同一对象。对于值类型,相等定义为按位相等。ValueType 类支持值类型。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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
//这里稍作更改,以证明值类型下的Equals方法:
char c1 = 'A';
char c2 = 'A';
Console.WriteLine(c1.Equals(c2)); //===> true
Obj1 = 'A';
Obj2 = 'A';
Console.WriteLine(Obj1.Equals(Obj2).ToString()); //===> true, 这里为子类实例指向父类的引用(多态性),实际上这里调用的子类的Equals()方法,即值类型的Equals方法
}
}
在默认情况下Equals只能在引用类型中用,但是在ValueType类型中已经重写了Equals方法,所以通常情况下Equals方法既可以用于引用类型,也可以应用于值类型;在引用类型中Equals方法比较的是引用相等,而在值类型中Equals方法比较的是值相等。
==运算符在MSDN中的解释:
对于预定义的值类型,如果操作数的值相等,则相等运算符 (==) 返回 true,否则返回 false。对于 string 以外的引用类型,如果两个操作数引用同一个对象,则 == 返回 true。对于 string 类型,== 比较字符串的值。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
// cs_operator_equality.cs
using System;
class MainClass
{
static void Main()
{
// Numeric equality: True
Console.WriteLine((2 + 2) == 4);//return true,这里比较的是值相等
// Reference equality: different objects,
// same boxed value: False.
object s = 1;
object t = 1;
Console.WriteLine(s == t);//return false,这里比较的是引用相等
Console.WriteLine(s.Equals(t).ToString());//return true,这里比较的是值相等(多肽)
// Define some strings:
string a = "hello";
string b = String.Copy(a); //注意:这里的Copy方法将在堆中创建一个新对象, 并将a的值拷贝到b中,也就是说a,b的值虽然相同,但是引用却不相同,a和b是不同的对象
string c = "hello"; //注意:这的操作设计到string的一个优化策略,对于string类型的两个变量a和c,如果系统发现a和c的内容相同,则直接将a的引用赋给c;也就是说a和c的引用指向的是同一对象。(相同的引用肯定有相同的值,但是相同值却不一定有相同的引用)
// Compare string values of a constant and an instance: True
Console.WriteLine(a == b);//return true,string比较特殊,string的很多方法被重写过, 这里比较的是值相等,即内容相等
Console.WriteLine(a.Equals(b) .ToString()); //return true,这里比较的是值相等,Equals()方法是string上的方法
// Compare string references;
// a is a constant but b is an instance: False.
Console.WriteLine((object)a == (object)b);//return false,这里比较的是引用相等,因为string的==操作被重写过,所以强转为object类型后再比较其引用。因为b创建新的对象, 所以虽然a和b的值相同,但终究是不同的引用
Console.WriteLine((object)a.Equals((object)b) .ToString()); //return true,这里比较的是值相等, Equals()方法是string上的方法(多肽)
// Compare string references, both constants
// have the same value, so string interning
// points to same reference: True.
Console.WriteLine((object)a == (object)c);//return true,这里的a和c的值相同,那么编译器认为a和c指向同一个引用
Console.WriteLine((object)a.Equals((object)c) .ToString()); //return true,这里比较的是值相等,Equals()方法是string上的方法(多肽)
}
}
string类型比较特殊,因为string类型的使用非常频繁,所以作为引用类型的string的很多方法都被重写过,以至于我们可以像使用值类型一样来使用string类型。
综上所述:
对于引用类型来说,java中的==比较的是引用,而Equals方法比较的是值(只为了考试而了解java,我个人不熟悉java,对真实情况不太确定。但至少那道题目的答案是这个意思)。
在.NET中,ReferenceEquals()方法是专门用来表示引用相等的;
==运算符和Equals()方法都可以用来表示引用相等和值相等。对于值类型而言, ==和Equals()方法是一样的,使用上没有什么区别。那么重点是引用类型:
从效果上说,对于引用类型, ==运算符表示引用相等(string有运算符重载,除外),而Equals()表示值相等。从效果上讲,这个说法好像不算错,网上也有这么一种说法。
从原理上说,无论是对值类型还是对于引用类型,==运算符和Equals()方法是相同的,当然排除了一个是运算符一个是方法的这个区别。为什么这样说呢?对于值类型而言,==运算符表示值相等,而Equals()方法在绝大多数的值类型中都重写了这个方法,使之能够支持值类型。在值类型这个层面的区别基本可以忽略。而对于引用类型而言, ==运算符表示引用相等(string除外),Equals()默认只支持引用类型(见第二个代码块),也表示引用相等。因此有==运算符既支持引用相等,也支持值相等,同样Equals()方法也支持值相等和引用相等, 所以==运算符和Equals()方法是相同的。那么为什么第三个代码块中的==运算符和Equals()方法的结果屡有不同呢?答案是多肽:子类实例指向父类引用,同种语义不同行为。虽然object为引用类型,而引用类型的Equals()方法的结果应该是引用相等,但由于多肽的存在,object类型的变量中实际存放的是值类型,所以调用的Equals()方法是子类实例的,因此才体现不相等的结果。也正因为如此,值相等还是引用相等和多肽这两个不相干的问题,硬是被说成了==运算符是引用相等而Equals()方法是值相等。
个人认为:==运算符和Equals()方法除了运算符和方法的区别外,再没有别的区别。而所谓的Equals()方法表示值相等的说法只是由于多肽而造成的假象。当然,Equals()用于值相等的说法效果上好像也说得过去。