实际上,在您的问题中看似简单的代码中有很多内容需要处理,因此让我们逐步进行。请注意,有很多东西正在进行,我可能会错过一些细节,请使用评论区提出。
首先处理第一段代码:
int value1 = 'a';
char value2 ='a';
Console.WriteLine(value1 == value2);
Console.WriteLine(value1.Equals(value2));
这行文字:
int value1 = 'a';
这应该能提示您此代码行为的原因。从char
到int
存在一个静默转换。实际上,编译后的内容并没有提到这个变量是一个char
,它是一个数字。赋给int
变量的数字是a
的码点值,即97。
第一个比较:
value1 == value2
在 int
上使用 ==
运算符进行比较,实际上是进行了纯数字比较,就好像字符是一个数字一样。这里也发生了相同的静默转换,字符被转换为数字。既然是相同的字符和相同的转换,那么从这个比较中你最终得到的结果也应该是 97。
这在规范的第 6.1.2 节“隐式数字转换”中提到:
隐式数字转换包括:
...
从 char
到 ushort
、int
、uint
、long
、ulong
、float
、double
或 decimal
这意味着实际上所写的等同于:
97 == 97
第二个比较:
value1.Equals(value2)
使用完全相同的转换进行完成,因此您拥有:
97.Equals(97)
因此,通过添加显式强制转换并更改代码以符合编译器的视图,让我们使第一段代码变得非常清晰:
// int value1 = (int)'a'; // 97
int value1 = 97;
char value2 = 'a';
Console.WriteLine(value1 == (int)value); // 97 == 97
Console.WriteLine(value1.Equals((int)value2)); // 97.Equals(97);
我还让LINQPad展示了这两个语句的反编译:
int a = 97;
int a = 'a';
它们都编译为:
ldc.i4.s 61 // 0x61 = 97
为了明确,这种静默转换是由编译器完成的,对于声明来说没有将字符转换成整数的运行时代码,代码执行并被编译就像您实际编写的一样。
int value1 = 97;
所以这就是这部分的原因。
现在转到下一部分:
object obj1 = "Object One";
object obj2 = new string("Object One".ToCharArray());
Console.WriteLine(obj1 == obj2);
Console.WriteLine(obj1.Equals(obj2));
在这里,您首先声明了两个object
变量,这一点非常重要,然后给它们赋相同的string
值,虽然它们是两个不同的实例。
那么让我们处理第一个比较:
obj1 == obj2
这是使用为object
定义的==
运算符完成的,它比较引用。由于我们已经确定了第二个变量的奇怪字符串构造会构建一个新实例,因此引用比较表明它们是不同的。
之所以使用在object
上定义的==
运算符,而不是在string
上定义的运算符,是因为运算符在编译时被解析,而编译器只知道变量是object
类型。它们包含字符串这一事实,甚至编译器可以“看到”您刚刚将字符串赋值给它们,以便应该使用string
==
运算符来代替,但被忽略了。
然而,当你这样做:
obj1.Equals(obj2)
那么您正在调用在object
中声明的虚拟.Equal(object other)
,该方法在string
中被重写,因此您得到了字符串内容比较,这表明它们是相同的。
因此让我们将第二段代码也表述得非常清楚:
object obj1 = "Object One";
object obj2 = new string("Object One".ToCharArray());
Console.WriteLine(obj1.ReferenceEquals(obj2)); // <-- changed
Console.WriteLine(obj1.Equals(obj2));
int
和char
在编译器中拥有一些神奇的支持,这就是为什么这两个之间允许出现的内容对于其他任何东西都不可表示。 - Lasse V. Karlsen