这是一小段代码:
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'
,因此在引用上不相等(并置操作在运行时完成)。
在这里查看反编译的代码
我还要补充一点,Try Roslyn进行了错误的反编译:-)甚至IlSpy :-(
它反编译为:
string expr_05 = "abc"
Console.WriteLine(expr_05 == "abc");
Console.WriteLine(expr_05 == "ab" + 'c');
所以字符串比较。但是至少可以清楚地看到一些字符串是在编译时计算出来的事实。
为什么您的代码进行参考比较?因为您要将两个成员之一转换为object
,而operator==
.NET中不是virtual
,所以必须在编译时使用编译器具有的信息进行解析,然后从==运算符中进行解析。
对于预定义的值类型,相等运算符(==)如果其操作数的值相等,则返回true,否则返回false。对于除字符串以外的引用类型,如果==的两个操作数引用同一对象,则==返回true。对于字符串类型,==比较字符串的值。
对于编译器,运算==
符的第一个操作数不是string
(因为您已将其强制转换),因此它不属于string
比较范围。
有趣的事实:在CIL级别(.NET的汇编语言)上,使用的操作码是ceq
,它对原始值类型进行值比较,对引用类型进行引用比较(因此最终,它始终进行逐位比较,但使用NaN的float类型除外)。它不使用“特殊”operator==
方法。在这个例子中可以看出
在哪里
Console.WriteLine(a == ("ab" + 'c')); // True
在调用时在编译时解决
call bool [mscorlib]System.String::op_Equality(string, string)
而另一个==
仅仅是
ceq
这解释了为什么Roslyn反编译器工作“非常差”(如IlSpy :-(,请参见bug报告)...。它看到一个操作码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);
这些二进制+运算符的重载执行字符串连接。如果字符串连接的操作数为null,则替换为空字符串。否则,通过调用从类型对象继承的虚拟ToString方法,将任何非字符串参数转换为其字符串表示形式。如果ToString返回null,则替换为空字符串。
因此,案件string + string
,string + null
,null + string
都精确地描述,并通过使用C#规格只有规则的结果可以“计算”。对于其他所有类型,都virtual ToString
必须调用该方法。virtual ToString
没有为C#规范中的任何类型定义该方法的结果,因此,如果编译器“假定”其结果,则会执行错误的“操作”。例如,对于C#规范System.Boolean.ToString()
,返回Yes
/No
而不是True
/的.NET版本False
仍然可以。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句