这是一小段代码:
String a = "abc";
Console.WriteLine(((object)a) == ("ab" + "c")); // true
Console.WriteLine(((object)a) == ("ab" + 'c')); // false
为什么 ?
最佳答案
因为==
正在做引用比较。使用C#编译器,将在编译时已知的所有“相等”字符串“分组”在一起,以便
string a = "abc";
string b = "abc";
将指向相同的“abc”字符串。因此,它们将在参照上相等。现在,
("ab" + "c")
在编译时简化为"abc"
,而"ab" + 'c'
不简化,因此引用不相等(串联操作在运行时完成)。参见反编译的代码here
我还要补充一点,Try Roslyn进行了错误的反编译:-)甚至IlSpy :-(
它反编译为:
string expr_05 = "abc"
Console.WriteLine(expr_05 == "abc");
Console.WriteLine(expr_05 == "ab" + 'c');
所以字符串比较。但是至少可以清楚地看到一些字符串是在编译时计算出来的事实。为什么您的代码进行引用比较?因为您要将两个成员之一转换为
object
,并且.NET中的operator==
不是virtual
,所以必须在编译时使用编译器具有的信息进行解析,然后从== Operator中进行解析。对于编译器,
==
运算符的第一个操作数不是string
(因为您已将其强制转换),因此它不属于string
比较。有趣的事实:在CIL级别(.NET的汇编语言)上,使用的操作码是
ceq
,它对原始值类型进行值比较,而对引用类型进行引用比较(因此最终,它总是逐位进行比较,但使用NaN的float类型除外)。它不使用“特殊” operator==
方法。可以在此example中看到在哪里
Console.WriteLine(a == ("ab" + 'c')); // True
在调用时在编译时解决call bool [mscorlib]System.String::op_Equality(string, string)
而其他==
只是ceq
这就解释了为什么Roslyn反编译器会“非常糟糕”地工作(如IlSpy :-(,请参阅bug report)...。它看到一个操作码ceq
,并且不检查是否需要强制转换来重建正确的比较。Holger问为什么编译器只在两个字符串文字之间进行加法运算...现在,以非常严格的方式阅读C#5.0规范,并考虑将C#5.0规范与.NET规范“分开”(使用C#5.0对于某些类/结构/方法/属性/...的先决条件的异常(exception),我们有:
因此,精确描述了
string + string
,string + null
和null + string
的情况,并且仅使用C#规范的规则就可以“计算”它们的结果。对于其他所有类型,必须调用virtual ToString
方法。在C#规范中,没有为任何类型定义virtual ToString
方法的结果,因此,如果编译器“假定”其结果,则会执行错误的“操作”。例如,对于C#规范,具有System.Boolean.ToString()
返回Yes
/No
而不是True
/False
的.NET版本仍然可以。关于c# - 为什么这两个字符串比较返回不同的结果?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29516327/