为什么将两个字符串作为对象进行比较会导致意外结果

Why comparing two strings as object causes unexpected result

考虑以下代码。

object str = new string(new char[] { 't', 'e', 's', 't' });
object str1 = new string(new char[] { 't', 'e', 's', 't' });
Console.WriteLine(str==str1); // false
Console.WriteLine(str.Equals(str1));  // true

我理解在这里工作的相等运算符,因为我们已经隐式转换为对象,相等运算符正在检查两者的引用是否相等并且 returns false。

但我对第二个感到困惑,返回 true 看起来像是在调用 String 类型提供的 Equals 覆盖实现,它检查字符串的内容是否相等。

我的问题是为什么它不检查运算符的内容相等性,它们的实际类型是字符串而不是对象。对吗?

而以下代码对两者都输出 ture:

object str = "test";
object str1 = "test";
Console.WriteLine(str==str1); // true
Console.WriteLine(str.Equals(str1)); // true

String.Equals 方法的帮助是作为备注给出的:

This method performs an ordinal (case-sensitive and culture-insensitive) comparison.

因此,比较是通过逐个字符检查字符串来完成的,因此给出 true。

我认为这是因为 String == operator only takes string types as parameters, while the .Equals methodobject 类型作为参数。

由于字符串 == 只接受 string 类型作为参数,重载决议选择对象 == 运算符用于比较。

与:

Console.WriteLine(str==str1); // false

在编译时确定要使用哪个 C# 预定义(正式)operator == 重载。由于 strstr1 声明 object,因此选择重载 operator ==(object, object)。这是在编译时固定的。只是因为实际的 运行 时间类型恰好更具体,所以这并没有改变。如果您想在 运行 时绑定,请改用 Console.WriteLine((dynamic)str == (dynamic)str1); /* true */

与:

Console.WriteLine(str.Equals(str1));  // true

您在 object 上调用了 虚拟 方法。虚拟意味着它将在 运行 时间到达任何 override 相关的地方。 class System.String 有一个覆盖,并且由于 str 将具有 运行 时间类型 System.String,"virtual dispatch" 将使用覆盖.


关于您问题底部的补充:由于字符串实习,情况有所不同。字符串驻留是一种优化,其中相同的物理实例用于值相同的形式上不同的字符串。当你有两个字符串,其值在源代码中给出时,字符串驻留将 "optimize" 并对 same 实例进行两次引用。这通常是无害的,因为字符串保证 不可变 。所以通常你不关心它是同一个实例还是另一个具有相同值的实例。但是在你的例子中,我们可以 "reveal" 实习。

注意:字符串实习与您的原始问题无关。只有在您向问题添加新示例后,字符串实习才变得相关。

这是因为 string interning;当你写:

object str = "test";
object str1 = "test";
Console.WriteLine(str==str1);

这按预期工作,因为两个字符串由编译器在内部静默复制到一个位置,因此两个指针实际上指向相同的对象

如果你从一个字符数组创建一个字符串,编译器不够聪明,无法理解你的意图,并且它等同于上面的内容,所以,作为一个字符串,一个引用类型,它们实际上是两个不同的内存中的对象

看看这篇文章:https://blogs.msdn.microsoft.com/ericlippert/2009/09/28/string-interning-and-string-empty/

Equals 方法在字符串中被重写,因此它比较字符串的实际内容而不是地址 == ( ReferenceEquals) 在你的情况下是这样的,因为类型是 object.

当 == 用于对象类型的表达式时,它将解析为 System.Object.ReferenceEquals。

Equals 只是一个虚拟方法,其行为也是如此,因此将使用覆盖版本(对于字符串类型,比较内容)。