关于c#:试图理解带对象的==运算符


Trying to understand == operator with objects

本问题已经有最佳答案,请猛点这里访问。
1
2
3
4
5
6
7
object a ="1";
object b ="1";
Console.WriteLine(a == b); // returns True

object c = 1;
object d = 1;
Console.WriteLine(c == d); // returns False

上面的代码返回整数和字符串的不同结果。我不明白为什么。有人能帮我理解这背后的原因吗?

那么,EDOCX1(operator)和EDOCX1(function)之间的区别是什么?


将整数声明为object将导致装箱操作。既然它们已装箱,相等运算符将执行引用比较,并且引用不同。但是,string类型定义了自己的相等运算符并执行值比较(即,"我的字符是否与其他字符串的字符相同?")

edit:per@enigmativity,我错过了两个字符串声明为object的事实。这使得事情稍微复杂一些。我之前关于字符串的陈述在这里是错误的,因为operator==不是多态的。比较返回true,因为这些字符串是内部的,这意味着它们实际上是同一个对象,所以引用比较返回true。


虽然Ed S已经回答了==检查引用是否相等的问题,但添加了同样的msdn链接。

For predefined value types, the equality operator (==) returns true if
the values of its operands are equal, false otherwise. For reference
types other than string, == returns true if its two operands refer to
the same object. For the string type, == compares the values of the
strings.

如果要比较对象,则可以使用Equals方法。

同样,当你问==和referenceequals之间的区别时,你应该注意到==正在过载,Equals正在覆盖。

所以如果你这么说

1
2
3
4
string x ="ABCD";
string y = 'A' +"BCD"; // ensure it's a different reference

if (x == y) { // evaluates to TRUE

因为用于比较变量x和y的方法是在编译时确定的。字符串是不可变的,因此使用==重载来支持字符串的值相等不会有任何危害。当编译器优化字符串文本时,它会发现xy都有相同的值,因此您只需要一个字符串对象。它是安全的,因为字符串在C中是不可变的。

然而,当使用Equals时,变量的类型在运行时根据变量x中的实际类型来确定。

1
2
3
4
object x ="ABCD";
object y = 'A' +"BCD"; // ensure it's a different reference

if (x == y) { // evaluates to FALSE

反之

1
2
3
4
object x ="ABCD";
object y = 'A' +"BCD"; // ensure it's a different reference

if (x.Equals(y)) { // evaluates to TRUE

此外,您还可以检查重写equals()和operator==(C编程指南)的指导原则。

In C#, there are two different kinds of equality: reference equality
(also known as identity) and value equality. Value equality is the
generally understood meaning of equality: it means that two objects
contain the same values. For example, two integers with the value of 2
have value equality. Reference equality means that there are not two
objects to compare. Instead, there are two object references and both
of them refer to the same object.

[...]

By default, the operator == tests for reference equality by
determining whether two references indicate the same object.
Therefore, reference types do not have to implement operator == in
order to gain this functionality. When a type is immutable, that is,
the data that is contained in the instance cannot be changed,
overloading operator == to compare value equality instead of reference
equality can be useful because, as immutable objects, they can be
considered the same as long as they have the same value. It is not a
good idea to override operator == in non-immutable types.


此代码返回false

1
2
3
4
object a ="a";
object b = a +"b";
a ="ab";
Console.WriteLine(a == b); // returns False

此代码返回true

1
2
3
object a ="ab";
object b ="ab";
Console.WriteLine(a == b); // returns True

但在这两种情况下,ab的值都是"ab"。不同之处在于,对于第二组代码,编译器优化代码并使用相同的字符串。

因此,字符串作为对象正在将==作为引用等于进行计算。没有区别。


c中的==标记用于表示两个运算符:可重载的相等测试运算符和不可重载的引用等价测试运算符。在为两个操作数定义相等测试重载的情况下,它将使用前一个运算符;否则,它将尝试使用后一个运算符。由于Object没有定义任何相等测试重载,c解释==标记为引用第二个(引用等效)运算符。

示例中字符串比较相等的原因不是生成的代码正在检查其内容,而是与实现字符串文本的方式有关。编译程序集时,编译器将生成一个包含所有出现在其中的字符串文本的列表;该列表中所有字符串的长度和内容都作为一个blob包含在生成的程序集中。在代码中的每个位置使用字符串文字,编译器插入一条指令"加载对第*n*个字符串的引用",在生成所有代码后,编译器在程序集中包含其中包含的每个字符串文字的字符序列。加载程序集时,.NET运行时将创建一个包含String引用的表,为该程序集中定义的所有字符序列生成String实例,并将对新创建的字符串的引用存储在表中。因此,将变量设置为字符串文字"george"并不会创建新的字符串对象,而是使变量标识一个对象,该对象包含第一次加载代码时创建的六个字符序列g-e-o-r-g-e。

如果编译器和/或运行时碰巧注意到同一个字符序列出现在多个字符串文字中,则通过引用为较早的字符串生成的String对象,可以满足对保存该序列的较新文字的请求。一般来说,代码的编写方式应确保在发生这种替换时不必担心。在源文件中多次出现文本的情况下,编译器几乎肯定会用对同一个字符串实例的引用替换所有出现的文本,但编写良好的代码不应依赖于此。

在您的示例中,文字"1"的所有相关用法都出现在同一个文件中,因此被替换为对同一字符串对象的引用,但完全可能存在包含单个字符1的其他String实例;使用==运算符比较字符串文字"1"对于类型为Object的变量,如果该变量标识其他字符串之一,则会生成false


定义如下:来自String.cs

1
2
public static bool operator == (String a, String b) {
       return String.Equals(a, b);

等方法的定义是:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public static bool Equals(String a, String b) {
            if ((Object)a==(Object)b) {
                return true;
            }

            if ((Object)a==null || (Object)b==null) {
                return false;
            }

            if (a.Length != b.Length)
                return false;

            return EqualsHelper(a, b);
        }