13

所以我在玩 C#,看看它是否与这篇文章中的 C++ 行为相匹配:http: //herbsutter.com/2013/05/22/gotw-5-solution-overriding-virtual-functions/ 当我遇到这个非常奇怪的行为:

public class BaseClass
{
    public virtual void Foo(int i)
    {
        Console.WriteLine("Called Foo(int): " + i);
    }

    public void Foo(string i)
    {
        Console.WriteLine("Called Foo(string): " + i);
    }
}

public class DerivedClass : BaseClass
{
    public void Foo(double i)
    {
        Console.WriteLine("Called Foo(double): " + i);
    }
}

public class OverriddenDerivedClass : BaseClass
{
    public override void Foo(int i)
    {
        base.Foo(i);
    }

    public void Foo(double i)
    {
        Console.WriteLine("Called Foo(double): " + i);
    }
}

class Program
{
    static void Main(string[] args)
    {
        DerivedClass derived = new DerivedClass();
        OverriddenDerivedClass overridedDerived = new OverriddenDerivedClass();

        int i = 1;
        double d = 2.0;
        string s = "hi";

        derived.Foo(i);
        derived.Foo(d);
        derived.Foo(s);

        overridedDerived.Foo(i);
        overridedDerived.Foo(d);
        overridedDerived.Foo(s);
    }
}

输出

Called Foo(double): 1
Called Foo(double): 2
Called Foo(string): hi
Called Foo(double): 1
Called Foo(double): 2
Called Foo(string): hi

因此,显然它有利于隐式转换的 int 比基类中更具体的 Foo(int) 翻倍。或者它是否从基类中隐藏了 Foo(int) ?但是然后:为什么 Foo(string) 不隐藏?感觉很不一致......我是否覆盖 Foo(int) 也没关系;结果是一样的。谁能解释这里发生了什么?

(是的,我知道在派生类中重载基方法是不好的做法——Liskov 等等——但我仍然不希望 OverriddenDerivedClass 中的 Foo(int) 不被调用?!)

4

1 回答 1

8

为了解释它是如何在OverriddenDerivedClass示例中工作的:

在此处查看用于成员查找的 C# 规范:http: //msdn.microsoft.com/en-us/library/aa691331%28VS.71%29.aspx

这定义了如何完成查找。

特别是看这部分:

首先,构造在 T 中声明的所有可访问(第 3.5 节)成员 N 和 T 的基本类型(第 7.3.1 节)的集合。包含覆盖修饰符的声明被排除在集合之外。

在你的情况下,NFoo()。因为Declarations that include an override modifier are excluded from the set那时override Foo(int i)被排除在集合之外。

因此,只有未被覆盖的Foo(double i)仍然存在,因此它是被调用的那个。

这就是OverriddenDerivedClass示例的工作原理,但这不是DerivedClass示例的解释。

为了解释这一点,请查看规范的这一部分:

接下来,从集合中移除被其他成员隐藏的成员。

Foo(double i)in从基类中DerivedClass隐藏了Foo(int i),所以它被从集合中删除了。

这里棘手的部分是:

从集合中删除与在 S 的基类型中声明的 M 具有相同签名的所有方法。

你可能会说“但是等等!Foo(double i) 没有与 相同的签名Foo(int i),所以它不应该从集合中删除!”。

但是,由于存在从 int 到 double 的隐式转换,它认为具有相同的签名,因此从集合Foo(int i) 中删除。

于 2013-05-23T08:46:46.033 回答