为什么将两个字符串作为对象进行比较会导致意外结果
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
method 将 object
类型作为参数。
由于字符串 ==
只接受 string
类型作为参数,重载决议选择对象 ==
运算符用于比较。
与:
Console.WriteLine(str==str1); // false
在编译时确定要使用哪个 C# 预定义(正式)operator ==
重载。由于 str
和 str1
被 声明 为 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 只是一个虚拟方法,其行为也是如此,因此将使用覆盖版本(对于字符串类型,比较内容)。
考虑以下代码。
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
method 将 object
类型作为参数。
由于字符串 ==
只接受 string
类型作为参数,重载决议选择对象 ==
运算符用于比较。
与:
Console.WriteLine(str==str1); // false
在编译时确定要使用哪个 C# 预定义(正式)operator ==
重载。由于 str
和 str1
被 声明 为 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 只是一个虚拟方法,其行为也是如此,因此将使用覆盖版本(对于字符串类型,比较内容)。