在C#中,Object.Equals是虚方法,但Object.operator==没有使用它?

5

我遇到了C#中一个奇怪的“不对称”问题,我并不真正理解。请看以下代码:

using System;
using System.Diagnostics;
namespace EqualsExperiment
{
    class Program
    {
        static void Main(string[] args)
        {
            object apple = "apple";
            object orange = string.Format("{0}{1}", "ap", "ple");
            Console.WriteLine("1");
            Debug.Assert(apple.Equals(orange));
            Console.WriteLine("2");
            Debug.Assert(apple == orange);
            Console.WriteLine("3");
        }
    }
}

对于所有的.NET专家来说可能很明显,但是第二个断言失败了。

在Java中,我学到==是一种称为Object.ReferenceEquals的东西的同义词。在C#中,我认为Object.operator==使用Object.Equals,它是虚拟的,因此在System.String类中被重写。

有人能解释一下,为什么第二个断言在C#中失败了吗?我的哪些假设是错误的?


我在另一个帖子中找到了我的问题的答案。看起来object.operator==使用object.ReferenceEquals,但string.operator==使用object.Equals。这对我来说很反直觉,因为object.Equals是虚拟的,所以它已经可以在object.operator==中使用了。 - wigy
2个回答

8

运算符被定义为静态方法,因此它们无法参与多态。因此,您的第二个断言使用了object==定义(因为您的变量声明为object),它仅测试引用相等性。如果变量声明为string,则将使用string==重载,并且第二个断言将成功。


2
值得注意的是,为了完整起见,在这种情况下静态的 object.Equals(apple,orange) 会返回 trueobject.Equals 首先使用 == 检查引用相等性,如果失败了,它将使用重载的 apple.Equals(orange)(假设 appleorange 不是 null)。 - LukeH
我知道我使用了 static bool Object.operator==(Object, Object)。但我仍然不明白,为什么不调用 Object.Equals(Object)。因为它是虚拟的,最终会调用正确的 String.Equals(Object) - wigy

7
==运算符不是同义词,它是为不同类型定义的运算符。 ==运算符适用于字符串,并且它实际上使用了Equals方法:
public static bool operator ==(string a, string b) {
  return Equals(a, b);
}

然而,在你的代码中,你并没有在字符串上使用运算符,而是在对象上使用它,所以你得到的是为对象定义的==运算符,它使用ReferenceEquals进行比较。

要使用哪个运算符重载是在编译时决定的,因此它是变量类型决定了重载,而不是变量指向的对象的实际类型。


嗯,你说Object.operator==是用ReferenceEquals定义的,而String.operator==是用Equals定义的。这不是很好和直观吗? - wigy
1
@wigy:大多数情况下它运行得非常好,但当然也有像你的例子一样的情况,你可能希望比较在运行时确定。这肯定比例如C++和Java中不可靠地使用字符串的==运算符更直观。在VB中,=运算符在运行时确定比较,这更符合VB的工作方式,但这会导致其他几种情况让你感到惊讶。在C#中,可以从代码中确定将使用哪种比较,这更符合C#总体的工作方式。 - Guffa
谢谢Guffa,我知道它比C、Java或VB更好。C++运算符可能是虚拟的,所以你会遇到不同的问题。这就是为什么大多数C++编码标准使用Yoda条件,如 if (5 == something) {...}。从你的回答中,我了解到框架设计者认为ReferenceEquals对某些编码人员来说更直观,但另一方面修复了string.operator==。 - wigy
字符串内部化是否会使得 == 也可能返回 true?有什么想法吗? - Ani
@Ani:是的,如果你比较两个具有相同值的已经interned的字符串,它们将指向同一个字符串对象。在OP的例子中,“orange”字符串是故意在运行时创建的,以便它不被interned。 - Guffa
在 vb.net 中,只有当类型明确重载该运算符时才可以使用 = 运算符来比较。对于引用相等性,使用 Is,它除了将可空类型与 Nothing 比较外,永远不会表示其他任何意义。在我的看法中,这比 C# 更好,因为 == 有时表示引用相等性,有时表示值相等性。 - supercat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接