C#中方法重载的不同行为

Posted

技术标签:

【中文标题】C#中方法重载的不同行为【英文标题】:Different behaviour of method overloading in 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();
    

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

【问题讨论】:

【参考方案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) 没有在派生类中声明(即使它被覆盖,这不算在内)。

以上是关于C#中方法重载的不同行为的主要内容,如果未能解决你的问题,请参考以下文章

C# 方法的重载

在C#中,关于重载方法的说法正确的是(D)。 (选择一项)

C# 方法重载

C#基本语法学习

C#视频方法重载函数重载传值

在派生类中重载基方法