【问题标题】:Different behaviour of method overloading in C#C#中方法重载的不同行为
【发布时间】:2011-02-18 18:44:30
【问题描述】:

我在浏览 C# Brainteasers (http://www.yoda.arachsys.com/csharp/teasers.html) 时遇到了一个问题:这段代码的输出应该是什么?

class Base
{
    public virtual void Foo(int x)
    {
        Console.WriteLine ("Base.Foo(int)");
    }
}

class Derived : Base
{
    public override void Foo(int x)
    {
        Console.WriteLine ("Derived.Foo(int)");
    }

    public void Foo(object o)
    {
        Console.WriteLine ("Derived.Foo(object)");
    }
}

class Test
{
    static void Main()
    {
        Derived d = new Derived();
        int i = 10;
        d.Foo(i);  // it prints ("Derived.Foo(object)"
    }
} 

但是如果我把代码改成

class Derived 
{
    public void Foo(int x)
    {
        Console.WriteLine("Derived.Foo(int)");
    }

    public void Foo(object o)
    {
        Console.WriteLine("Derived.Foo(object)");
    }
}

class Program
{
    static void Main(string[] args)
    {
        Derived d = new Derived();
        int i = 10;
        d.Foo(i); // prints  Derived.Foo(int)");

        Console.ReadKey();
    }
}

我想知道为什么当我们继承而不是继承时输出会发生变化;为什么在这两种情况下方法重载的行为不同?

【问题讨论】:

    标签: c# inheritance overloading


    【解决方案1】:

    正如我在answers 页面中指定的那样:

    Derived.Foo(object) 被打印 - 选择重载时,如果有任何兼容 在派生类中声明的方法,在基类中声明的所有签名都将被忽略 - 即使它们在同一个派生类中被覆盖!

    换句话说,编译器会查看在最派生类中新声明的方法(基于表达式的编译时类型),并查看是否有适用的方法。如果是,它会使用“最好的”可用的。如果不适用,它会尝试基类,依此类推。被覆盖的方法不算在派生类中声明。

    有关详细信息,请参阅 C# 3 规范的第 7.4.3 和 7.5.5.1 节。

    现在至于为什么要这样指定 - 我不知道。对我来说,派生类中声明的方法优先于基类中声明的方法是有道理的,否则你会遇到“脆弱的基类”问题——在基类中添加一个方法可能会改变代码的含义,使用派生类。但是,如果派生类覆盖基类中声明的方法,它清楚地意识到这一点,因此脆性元素不适用。

    【讨论】:

    • 原谅我的无知,但是为什么它考虑使用“object”类型参数的方法是最好的,我认为 int 更接近(就像第二种情况下发生的那样)
    • @Wondering: 是的,int 更好——但是int 重载已经覆盖了基类中的方法,所以它没有被考虑:它不算是在派生类中“新声明”。这就是所有这些解释的重点:)
    【解决方案2】:

    它围绕范围。在第一个程序中,void Foo(int i) 属于类 Base。类 Derived 只是重新定义了它的行为。

    Foo(int i) 被忽略,因为它被“借用”并通过从类 Base 继承重新定义。如果 Foo(object o) 不存在,则使用 Foo(int i)。

    在第二个程序中,调用 void Foo(int i) 是因为它正确地属于 Derived 类(即它没有通过继承被借用和覆盖)并且具有最适合的签名。

    【讨论】:

      【解决方案3】:

      这是因为在考虑基类签名之前,先匹配派生类中的方法签名。因此,当您尝试时:

      d.Foo(i);
      

      它会尝试将方法签名与您当前的类(而不是基类)进行匹配。它找到一个可接受的匹配 Foo(object) 并且不进一步考虑基类方法签名。

      这完全是关于编译器找到匹配的方法签名,以及它用来查找这些签名的搜索顺序。

      -道格

      【讨论】:

      • 令人惊讶的是它在派生类中包含重写方法。这仍然是“派生类中的方法签名”,但不算在派生类中声明。
      • "它找到了一个可接受的匹配 Foo(object)" 但是,Foo(int) 也是可接受的匹配
      • 是的,但正如 Jon 所说,Foo(int) 没有在派生类中声明(即使它被覆盖,这不算在内)。
      猜你喜欢
      • 1970-01-01
      • 2013-06-21
      • 2015-12-04
      • 1970-01-01
      • 1970-01-01
      • 2010-09-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多