【问题标题】:Why comparing two strings as object causes unexpected result为什么将两个字符串作为对象进行比较会导致意外结果
【发布时间】:2016-11-15 23:30:26
【问题描述】:

考虑以下代码。

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

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

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

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

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

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

【问题讨论】:

  • 对函数或变量的调用总是尽可能接近对象的实际类。对Equals 的调用因此调用string.Equals,它被定义为比较字符串的内容,而不是默认的object.Equals。如果您绝对想使用objectEquals,可以使用(str as object)?.Equals(str1 as object)
  • @khlr 您应该补充说,作为答案,当前存在的问题有点错过了为什么它正在发生
  • 但是错了——字符串不是值类型。
  • @khlr string 不是真正的值类型;它是一个不可变的引用类型,它覆盖 Equals 并具有 C# 预定义的重载 operator ==。编辑:马修打败了我。
  • @Jeppe 啊,当然,你是对的!

标签: c# inheritance equals equality


【解决方案1】:

与:

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 上调用 virtual 方法。虚拟意味着它将转到运行时相关的任何overrideSystem.String 类有一个覆盖,由于str 将具有运行时类型System.String,因此“虚拟调度”将使用覆盖。


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

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

【讨论】:

  • 哇,这对于 OO 语言来说似乎是一个可怕的错误。说真的,调用的方法不应该是基类方法,因为您已将对象声明为基类的实例。毛。
  • 为什么在编辑之间另一个答案暂时错误的结束评论?它不会让你的答案更好。
  • @SethBattin 你是对的。我删除了其他答案的提及。我仍然注意到字符串实习对最后一部分很重要,但对第一部分不重要。
  • @ThomasAndrews 你错过了正在发生的事情。编译器已确定operator ==(object, object) 是最合适的匹配项。它绝对不会将 object 传递给 string 期望方法!
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

    这是因为 字符串实习;当你写:

    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

    【讨论】:

    • @HenkHolterman 如果您阅读了我的全部答案,您会看到它写在那里。
    • 我指的是第一部分,因为人们期望字符串等于另一个字符串,因为它们在引用类型时具有相同的字符。 “发生这种情况”意味着比较的 true 值仅在实习时发生。
    • 答案并不是说:这总是会发生,也许写“有时”会为自己省去这个无用的无休止的讨论。
    【解决方案4】:

    我相信这是因为String == operator 只将string 类型作为参数,而.Equals methodobject 类型作为参数。

    由于字符串==只接受string类型作为参数,重载决策选择对象==操作符进行比较。

    【讨论】:

      【解决方案5】:

      String.Equals 方法的帮助是这样的:

      此方法执行一个序数(区分大小写和 文化不敏感)比较。

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

      【讨论】:

      • @GrantWinney 因为Equals 是一种虚拟方法,并且String 具有Equals 的覆盖,所以String.Equals 是最终被调用的方法。但是,==Object 版本。
      猜你喜欢
      • 2011-10-07
      • 2022-01-25
      • 2010-10-22
      • 2021-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-13
      • 2012-11-17
      相关资源
      最近更新 更多