编译器选择错误的重载调用IEquatable<T>.Equals。

5
在一个对性能要求较高的程序中,我试图显式地调用 IEquatable<T>.Equals() 而不是 Object.Equals(在我的情况下避免装箱)。尽管我尽力了,但编译器总是选择 Object.Equals(),这让我很困惑。以下是一个人为构造的示例类:
class Foo : IEquatable<Foo>
{
    public bool Equals(Foo f)
    {
        Console.WriteLine("IEquatable.Equals");
        return true;
    }

    public override bool Equals(object f)
    {
        Console.WriteLine("Object.Equals");
        return true;
    }
}

同样虚构的代码展示了这个问题:
// This calls IEquatable<Foo>
Foo f = new Foo();
f.Equals(f);

// This calls Object.Equals
IEquatable<Foo> i = new Foo();
i.Equals(i);

这段代码的输出结果是:
IEquatable.Equals
Object.Equals

我阅读了Jon Skeet的有关重载的文章,但仍然不理解这里的问题。所以我的问题是,我如何在上面的变量i中显式调用IEquatable<Foo>.Equals


6
一个 Foo IEquatable<Foo>。但是,一个 IEquatable<Foo> 不是 Foo。这是否让你更清楚它为什么会按照这种方式工作 - 具体来说,为什么 public bool Equals(Foo f) 不能使用 IEquatable<Foo> 调用? - RB.
如果你确定 i 是一个 Foo,那么可以使用 i.Equals((Foo)i);。但是,当你对其类型如此确定时,i 的声明类型不就是 Foo 吗? - Damien_The_Unbeliever
1
说句题外话,我可以说一下这是一个多么棒的第一个问题吗?你清晰地陈述了你的问题,提供了可重现的代码、输出结果、期望输出的明确说明,最后还展示了独立的研究 - 给你点赞,欢迎来到 Stack Overflow :) - RB.
1
进一步解释RB所说的,有两种Equals方法可供选择:Equals(Foo other)Equals(object obj)。由于给定的参数i被定义为IEquatable<Foo>类型,因此必须选择对象重载。 - Pieter Witvoet
1
那是一个非常好的评论,你应该把它写成答案。 - Eldar Dordzhiev
1个回答

5
第二个重载函数被选择的原因与调用者的类型无关,而是与传递给Equals的参数类型有关。因此,即使您调用f.Equals(i),也会选择object.Equals方法。原因很简单,编译器会寻找最合适的重载函数。由于IEquatable<Foo>不一定是Foo,因为可能存在另一种类型,比如Bar,它实现了IEquatable<Foo>,在这种情况下,选择Equals(Foo f)重载就不正确(或不可能)了。
由于编译器不检查IEquatable<Foo>的底层类型,如果您想调用Equals(Foo)重载,需要显式将参数转换为Foo

感谢您的精彩解释。这使得我完全理解并且解决了问题。 - user5876832

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