为什么这两个字符串比较return不同的结果?

Why do these two string comparisons return different results?

这是一小段代码:

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' 不是,因此在引用上不相等(串联操作在运行时完成)。

查看反编译代码here

我要补充一点,Try Roslyn 反编译错误 :-) 甚至 IlSpy :-(

正在反编译为:

string expr_05 = "abc"
Console.WriteLine(expr_05 == "abc");
Console.WriteLine(expr_05 == "ab" + 'c');

所以字符串比较。但至少可以清楚地看到一些字符串是在编译时计算的。

为什么你的代码要进行引用比较?因为您将两个成员之一转换为 object,而 .NET 中的 operator== 不是 virtual,所以它必须在编译时使用编译器拥有的信息进行解析,然后...来自 == Operator

For predefined value types, the equality operator (==) returns true if the values of its operands are equal, false otherwise. For reference types other than string, == returns true if its two operands refer to the same object. For the string type, == compares the values of the strings.

对于编译器,== 运算符的第一个操作数不是 string(因为你强制转换了它),所以它不属于 string 比较.

有趣的事实:在 CIL 级别(.NET 的汇编语言),使用的操作码是 ceq,它对原始值类型进行值比较,对引用类型进行引用比较(所以在end 它总是进行逐位比较,但带有 NaN 的浮点类型除外)。它不使用“特殊”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 对某些 classes/structs/methods/properties/... 的先决条件除外),我们有:

String concatenation:

string operator +(string x, string y);
string operator +(string x, object y);
string operator +(object x, string y);

These overloads of the binary + operator perform string concatenation. If an operand of string concatenation is null, an empty string is substituted. Otherwise, any non-string argument is converted to its string representation by invoking the virtual ToString method inherited from type object. If ToString returns null, an empty string is substituted.

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

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