为什么这两个string比较返回不同的结果?

这是一小段代码:

String a = "abc"; Console.WriteLine(((object)a) == ("ab" + "c")); // true Console.WriteLine(((object)a) == ("ab" + 'c')); // false 

为什么?

因为==正在做一个参考比较。 使用C#编译器,所有在编译时已知的“相等”string被“分组”在一起,所以

 string a = "abc"; string b = "abc"; 

将指向相同的“abc”string。 所以他们会是平等的。

现在("ab" + "c")在编译时简化为"abc" ,而"ab" + 'c'不是,所以不等于(在运行时连接操作)。

在这里查看反编译的代码

我会补充一点,Try Roslyn做了一个错误的反编译:-)甚至IlSpy 🙁

它正在反编译为:

 string expr_05 = "abc" Console.WriteLine(expr_05 == "abc"); Console.WriteLine(expr_05 == "ab" + 'c'); 

所以string比较。 但至less在编译时计算一些string的事实可以清楚地看到。

为什么你的代码做参考比较? 因为你投了两个成员之一来object ,而在.NET中的operator==不是virtual ,所以它必须在编译时与编译器的信息解决,然后…从==运算符

对于预定义的值types,如果操作数的值相等,则相等运算符(==)返回true,否则返回false。 对于string以外的引用types,如果其两个操作数引用同一个对象,则==返回true。 对于stringtypes,==比较string的值。

对于编译器来说, ==运算符的第一个操作数不是一个string (因为它是铸造的),所以它不属于string比较。

有趣的事实:在CIL级别(.NET的汇编语言),所使用的操作码是ceqceq原始值types进行了值比较,并对参考types进行了参考比较(所以最后它总是按位比较,有一些与NaN的floattypes的例外)。 它不使用“特殊” operator==方法。 在这个例子中可以看出

哪里

 Console.WriteLine(a == ("ab" + 'c')); // True 

在编译时通过调用来解决

 call bool [mscorlib]System.String::op_Equality(string, string) 

而其他==是简单的

 ceq 

这就解释了为什么Roslyn反编译器“不好”的工作(如IlSpy :-(,见bug报告 )…它看到一个操作码ceq ,并且不检查是否需要重build正确的比较。

Holger问道为什么只有两个string之间的加法是由编译器完成的……现在,以一种非常严格的方式阅读C#5.0规范,并且考虑到C#5.0规范与.NET规范“分离” C#5.0对某些类/结构体/方法/属性/ …所具有的先决条件的exception),我们有:

string连接:

 string operator +(string x, string y); string operator +(string x, object y); string operator +(object x, string y); 

这些二进制运算符的重载执行string连接。 如果string连接的操作数为空,则replace空string。 否则,通过调用从objecttypesinheritance的虚拟ToString方法,将任何非string参数转换为其string表示forms。 如果ToString返回null,则replace空string。

所以,case string + stringstring + nullnull + string都被精确地描述,并且它们的结果可以通过仅使用C#规范的规则来“计算”。 对于其他types,必须调用virtual ToString方法。 对于C#规范中的任何types, virtual ToString方法的结果都没有定义,所以如果编译器“推测”了它的结果,它会做错误的“事情”。 例如,具有System.Boolean.ToString()返回Yes / No而不是True / False的.NET版本对于C#规范仍然可以。

地址不一样。 如果你想比较一个string,build议使用equals。