C# 中 == 运算符和 Equals() 方法的区别?

Posted

技术标签:

【中文标题】C# 中 == 运算符和 Equals() 方法的区别?【英文标题】:Difference between == operator and Equals() method in C#? 【发布时间】:2012-03-20 17:27:40 【问题描述】:

==Equals() 有什么区别?我知道==用于比较运算符,Equals()方法用于比较字符串的内容。所以我尝试了

// first example
string s1 = "a";
string s2 = "a";
Console.Write(a.Equals(s2)); // returns true, but if I assign "b" to s2,
                             // then result will be false

// second example
string s1 ="a";
string s2 ="a";
Console.Write(s1 == s2);     // returns true

这是怎么回事?两者都是不同的对象引用。假设我们认为这些是参考。但是我尝试这样使用

string s1 = new string("ab");
string s2 = new string("ab");

我收到无法将字符串转换为字符的编译时错误

【问题讨论】:

Equals()方法只是用来比较一个字符串的内容,它可以用来比较any两个对象。 C# 中的string 类型碰巧 包含Equals() 的覆盖方法,该方法将比较它们的值(这一事实在许多文章中解释得很差,这些文章表明Equals() 用于字符串仅供比较)。 【参考方案1】:

有几件事正在发生。首先,在这个例子中:

string s1 = "a";
string s2 = "a";
Console.WriteLine(s1 == s2);

您声称:

两者都是不同的对象引用。

由于字符串实习,这不是真的。 s1s2 是对同一个对象的引用。 C# 规范保证 - 从 C# 4 规范的第 2.4.4.5 节:

当根据字符串相等运算符(第 7.10.7 节)等效的两个或多个字符串文字出现在同一程序中时,这些字符串文字引用相同的字符串实例。

因此在这种特殊情况下,即使您打印了object.ReferenceEquals(s1, s2),或者如果您使用与== 的真实参考身份比较,您仍然会得到“真实”:

object s1 = "a";
object s2 = "a";
Console.WriteLine(s1 == s2); // Still prints True due to string literal interning

然而,即使这些对单独对象的引用,== 对于string 也会重载。重载是一个编译时决定 - 要使用的实现取决于操作数的编译时类型。比如:

string a = new string('x', 1);
string b = new string('x', 1);
Console.WriteLine(a == b); // Uses string's implementation, prints True

object c = a;
object d = b;
Console.WriteLine(c == d); // Reference identity comparison, prints False

将其与 object.Equals(object) 进行比较,后者是一个虚拟方法。碰巧,String 重载了这个方法以及,但重要的是它覆盖了它。因此,如果我们将代码更改为:

string a = new string('x', 1);
string b = new string('x', 1);
Console.WriteLine(a.Equals((object) b));

object c = a;
object d = b;
Console.WriteLine(c.Equals(d));

...那么编译代码中的两个方法调用都将简单地指向object.Equals(object),但由于多态性,它们仍然都会打印True:将使用String中的实现.

overloaded 方法的调用如下所示:

string a = new string('x', 1);
string b = new string('x', 1);
Console.WriteLine(a.Equals(b)); // Calls string.Equals(string)

【讨论】:

两个问题:为什么要把d 转换成object,因为它已经是一个对象了?另外,在a Equals b 部分,我会同时调用对象覆盖和字符串重载,因此更清楚区别是什么。 @doekman: 1) 是的,在这种情况下我不需要演员表。 2) 不确定你的意思。 我的意思是:见最后一个答案。我没有直接得到你关于重载/覆盖的解释......【参考方案2】:

引用Equals的文档:

Equals 的默认实现支持引用相等 引用类型和值类型的按位相等。参考 相等意味着被比较的对象引用引用 同一个对象。按位相等意味着被比较的对象有 相同的二进制表示。

还有== operator:

对于预定义的值类型,相等运算符 (==) 在以下情况下返回 true 其操作数的值相等,否则为假。以供参考 字符串以外的类型,如果两个操作数引用 ==,则返回 true 同一个对象。对于字符串类型,== 比较 字符串。

现在回到你的问题:为什么s1 == s2 返回真?字符串是 .NET 中的特殊野兽。它们代表不可变的引用类型。他们在 .NET 中实习。这意味着如果您有 2 个具有相同值的字符串常量,它们将在运行时引用相同的对象实例。

引用自documentation:

公共语言运行时通过维护一个 表,称为实习生池,其中包含对 以编程方式声明或创建的每个唯一文字字符串 你的程序。因此,文本字符串的实例具有 特定值在系统中只存在一次。

【讨论】:

不是单独因为实习。实习就是为什么即使它们被声明为object 类型也会起作用。但是,由于运算符重载,只要涉及的编译时类型都是字符串,即使对具有相同内容的不同字符串对象的引用也可以工作。您引用的文档具有误导性,因为它表明存在一些特殊处理 仅针对字符串 - 没有,只​​是字符串是 a 引用类型,它重载了 = = 运算符。 另请注意,实习是一种语言细节——.NET 提供了机制,但很可能有一种语言 Dumb#,它在各方面都与 C# 相同,除了没有字符串常量实习。【参考方案3】:

你的想法似乎是 Java 风格的。在java中,==操作符不能自定义,所以对于引用类型,它总是表示引用相等,而对于原始类型它表示值相等。另一方面,Equals 用于检查引用类型中的值相等性。

不过,C# 中的情况有所不同。 Equals== 都可以有自定义实现。区别在于Equals 是虚拟(实例)方法,而operator== 是静态方法。除此之外,它们的行为方式完全相同。

默认情况下,Equals== 都检查引用类型的引用相等性和值类型的值相等性。但是,对于string,两者都被定制为检查值是否相等。

【讨论】:

以上是关于C# 中 == 运算符和 Equals() 方法的区别?的主要内容,如果未能解决你的问题,请参考以下文章

《C#高级编程》读书笔记:运算符

equals方法的使用

Java提高篇——equals()方法和“==”运算符

==运算符和equals()方法的区别

重写 Equals 方法时是不是需要重写 == 和 != 运算符? (。网)

[转]Java中==和equals的区别,equals和hashCode的区别