35

这是一小段代码:

String a = "abc";

Console.WriteLine(((object)a) == ("ab" + "c")); // true 
Console.WriteLine(((object)a) == ("ab" + 'c')); // false 

为什么 ?

4

2 回答 2

75

因为==是在做参考比较。使用 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,所以必须在编译时使用编译器拥有的信息来解析它,然后... from == Operator

对于预定义的值类型,相等运算符 (==) 如果其操作数的值相等则返回 true,否则返回 false。对于字符串以外的引用类型,== 如果它的两个操作数引用同一个对象,则返回 true。对于字符串类型,== 比较字符串的值。

对于编译器,运算符的第一个操作数==不是 a string(因为您已对其进行了转换),因此它不属于string比较范围。

有趣的事实:在 CIL 级别(.NET 的汇编语言),使用的操作码是ceq,它对原始值类型进行值比较,对引用类型进行引用比较(所以最后它总是进行逐位比较, 带有 NaN 的浮点类型有一些例外)。它不使用“特殊”operator==方法。在这个例子中可以看到

在哪里

Console.WriteLine(a == ("ab" + 'c')); // True 

在编译时通过调用解决

call bool [mscorlib]System.String::op_Equality(string, string)

而其他==的只是

ceq

这解释了为什么 Roslyn 反编译器工作“很糟糕”(作为 IlSpy :-(,请参阅错误报告)...它看到一个操作码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,则替换为空字符串。否则,通过调用从类型 object 继承的虚拟 ToString 方法,将任何非字符串参数转换为其字符串表示形式。如果 ToString 返回 null,则替换为空字符串。

因此,case string + string, string + null,null + string都被精确地描述了,并且它们的结果可以通过仅使用 C# 规范的规则来“计算”。对于其他所有类型,都virtual ToString必须调用该方法。该virtual ToString方法的结果没有为 C# 规范中的任何类型定义,所以如果编译器“假定”它的结果,它会做一个错误的“事情”。例如System.Boolean.ToString(),返回Yes/No而不是True/的 .NET 版本False仍然适用于 C# 规范。

于 2015-04-08T13:46:52.360 回答
-3

地址不一样。如果要比较字符串字符,建议使用equals。

于 2015-04-09T08:40:50.313 回答