为什么这两个字符串比较会返回不同的结果?

时间:2015-04-08 13:40:10

标签: c# string reference pre-compilation

这是一小段代码:

String a = "abc";

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

为什么?

2 个答案:

答案 0 :(得分:75)

因为==正在进行参考比较。使用C#编译器,编译时已知的所有“相等”字符串被“分组”在一起,因此

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

将指向相同的“abc”字符串。所以他们的参考是平等的。

现在,("ab" + "c")在编译时被简化为"abc",而"ab" + 'c'则不是,因此不是引用相等(连接操作在运行时完成)。

请参阅反编译代码here

我要补充一点,试试Roslyn正在做错误的反编译:-)甚至IlSpy: - (

正在反编译为:

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

所以字符串比较。但至少可以清楚地看到在编译时计算某些字符串的事实。

为什么您的代码正在进行参考比较?因为您要将两个成员中的一个转换为object,并且.NET中的operator==不是virtual,所以必须在编译时使用编译器具有的信息来解析它,然后......来自== Operator

  

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

对于编译器,==运算符的第一个操作数不是string(因为您已将其转换),因此它不属于string比较。< / p>

有趣的事实:在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对某些类/结构/方法/属性/ ...的先决条件之外),我们有:

  

字符串连接:

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

binary +运算符的这些重载执行字符串连接。如果字符串连接的操作数为null,则替换空字符串。否则,通过调用从类型对象继承的虚拟ToString方法,将任何非字符串参数转换为其字符串表示形式。如果ToString返回null,则替换空字符串。

因此,案例string + stringstring + nullnull + string都已准确描述,其结果可以通过仅使用C#规范的规则来“计算”。对于每个其他类型,必须调用virtual ToString方法。 virtual ToString方法的结果没有为C#规范中的任何类型定义,因此如果编译器“推测”其结果,它将执行错误的“事物”。例如,返回System.Boolean.ToString() / Yes而不是No / True的{​​{1}}的.NET版本对于C#规范仍然可以。

答案 1 :(得分:-3)

地址不一样。如果你想比较一个字符串字符,建议使用equals。