为什么这两个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的汇编语言),所使用的操作码是ceq
, ceq
原始值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 + string
, string + null
, null + string
都被精确地描述,并且它们的结果可以通过仅使用C#规范的规则来“计算”。 对于其他types,必须调用virtual ToString
方法。 对于C#规范中的任何types, virtual ToString
方法的结果都没有定义,所以如果编译器“推测”了它的结果,它会做错误的“事情”。 例如,具有System.Boolean.ToString()
返回Yes
/ No
而不是True
/ False
的.NET版本对于C#规范仍然可以。
地址不一样。 如果你想比较一个string,build议使用equals。