为什么这两个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。