这是一小段代码:
String a = "abc";
Console.WriteLine(((object)a) == ("ab" + "c")); // true
Console.WriteLine(((object)a) == ("ab" + 'c')); // false
为什么?
答案 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 + string
,string + null
,null + string
都已准确描述,其结果可以通过仅使用C#规范的规则来“计算”。对于每个其他类型,必须调用virtual ToString
方法。 virtual ToString
方法的结果没有为C#规范中的任何类型定义,因此如果编译器“推测”其结果,它将执行错误的“事物”。例如,返回System.Boolean.ToString()
/ Yes
而不是No
/ True
的{{1}}的.NET版本对于C#规范仍然可以。
答案 1 :(得分:-3)
地址不一样。如果你想比较一个字符串字符,建议使用equals。