为什么将两个字符串作为对象进行比较会导致意外的结果
本文关键字:比较 结果 意外 对象 字符串 两个 为什么 | 更新日期: 2023-09-27 17:56:49
请考虑以下代码段。
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 覆盖实现,如果它们相等,它会检查字符串的内容。
我的问题是为什么它不检查运算符的内容相等性,它们的实际类型是字符串而不是对象。 对吧?
而下面的代码输出两者:
object str = "test";
object str1 = "test";
Console.WriteLine(str==str1); // true
Console.WriteLine(str.Equals(str1)); // true
与:
Console.WriteLine(str==str1); // false
它是在编译时确定要使用的operator ==
的 C# 预定义(正式)重载。由于str
和str1
被声明为object
,因此选择重载operator ==(object, object)
。这是在编译时修复的。仅仅因为实际的运行时类型恰好更具体,这不会改变。如果要在运行时绑定,请改用Console.WriteLine((dynamic)str == (dynamic)str1); /* true */
。
跟:
Console.WriteLine(str.Equals(str1)); // true
您在 object
上调用虚拟方法。虚拟意味着它将转到运行时相关的任何override
。类System.String
有一个覆盖,并且由于str
将具有运行时类型System.String
,因此覆盖将由"虚拟调度"使用。
关于您问题底部的补充:由于字符串实习,这种情况有所不同。字符串实习是一种优化,其中相同的物理实例用于值相同的形式上不同的字符串。当您有两个字符串,其值在源代码中给出时,字符串实习将"优化"并对同一实例进行两次引用。这通常是无害的,因为字符串保证是不可变的。因此,通常您不关心它是相同的实例还是具有相同值的另一个实例。但是在你的例子中,我们可以"揭示"实习。
注意:字符串实习与您最初的问题无关。只有在您在问题中添加了新示例后,字符串实习才变得相关。
当 == 用于 object 类型的表达式时,它将解析为 System.Object.ReferenceEquals。
Equals 只是一个虚拟方法,其行为是这样的,因此将使用重写的版本(对于字符串类型,它比较内容)。
发生这种情况是因为字符串实习;当你写:
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) 在您的情况下是对象。
这是因为String
==
运算符仅将string
类型作为参数,而.Equals
方法将object
类型作为参数。
由于字符串==
仅将string
类型作为参数,因此重载解析会选择要用于比较的对象==
运算符。
String.Equals
方法的帮助是将此作为注释:
此方法执行序号(区分大小写和 不区分文化)比较。
因此,比较是通过逐个检查字符串字符来完成的,从而给出 true。