Pab*_*ney 35 c# string reference pre-compilation
这是一小段代码:
String a = "abc";
Console.WriteLine(((object)a) == ("ab" + "c")); // true
Console.WriteLine(((object)a) == ("ab" + 'c')); // false
Run Code Online (Sandbox Code Playgroud)
为什么?
xan*_*tos 75
因为==正在做参考比较.使用C#编译器,编译时已知的所有"相等"字符串被"分组"在一起,因此
string a = "abc";
string b = "abc";
Run Code Online (Sandbox Code Playgroud)
将指向相同的"abc"字符串.所以他们的参考是平等的.
现在,("ab" + "c")在编译时简化为"abc"while,而"ab" + 'c'不是,因此不是引用相等(连接操作在运行时完成).
请在此处查看反编译的代码
我要补充一点,试试Roslyn正在做一个错误的反编译:-)甚至IlSpy :-(
它反编译为:
string expr_05 = "abc"
Console.WriteLine(expr_05 == "abc");
Console.WriteLine(expr_05 == "ab" + 'c');
Run Code Online (Sandbox Code Playgroud)
所以字符串比较.但至少可以清楚地看到在编译时计算某些字符串的事实.
为什么您的代码进行参考比较?因为您要将两个成员中的一个转换为object,而operator==.NET中没有virtual,所以必须在编译时使用编译器具有的信息解析,然后...来自==运算符
对于预定义的值类型,如果操作数的值相等,则相等运算符(==)返回true,否则返回false.对于除string之外的引用类型,如果其两个操作数引用同一对象,则==返回true.对于字符串类型,==比较字符串的值.
对于编译器,运算==符的第一个操作数不是a string(因为你将其转换),因此它不属于string比较.
有趣的事实:在CIL级别(.NET的汇编语言),使用的操作码是ceq,对原始值类型进行值比较,对参考类型进行参考比较(所以最后它总是进行逐位比较,对于带NaN的float类型有一些例外).它不使用"特殊" operator==方法.在这个例子中可以看到
在哪里
Console.WriteLine(a == ("ab" + 'c')); // True
Run Code Online (Sandbox Code Playgroud)
在编译时在调用时解析
call bool [mscorlib]System.String::op_Equality(string, string)
Run Code Online (Sandbox Code Playgroud)
而另一个==则很简单
ceq
Run Code Online (Sandbox Code Playgroud)
这解释了为什么Roslyn反编译工作"非常糟糕"(如IlSpy :-(,请参阅错误报告)...它看到了一个操作码ceq,并没有检查是否需要重建正确的比较.
Holger问为什么只有两个字符串文字之间的加法是由编译器完成的...现在,以非常严格的方式阅读C#5.0规范,并考虑将C#5.0规范与.NET规范"分离"(使用C#5.0对某些类/结构/方法/属性/ ...的先决条件的例外情况,我们有:
字符串连接:
Run Code Online (Sandbox Code Playgroud)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#规范中的任何类型定义,因此如果编译器"推测"其结果,它将执行错误的"事物".例如System.Boolean.ToString(),返回Yes/ No而不是True/ 的.NET版本False对于C#规范仍然可以.