方法解析顺序

Tom*_*lan 10 c# overloading

假设我们有:

public class FooBase
{
    public void Write(byte value)
    {
        //something
    }

    public void Write(int value)
    {
        //something
    }
}

public class Foo : FooBase
{
    public void Write(decimal value)
    {
        //something
    }
}
Run Code Online (Sandbox Code Playgroud)

比这个:

        var writer = new Foo();

        writer.Write(5);         //calls Write(decimal) !!
        writer.Write((byte)6);   //calls Write(decimal) !!
Run Code Online (Sandbox Code Playgroud)

将调用Write(十进制)重载.为什么?我怎样才能调用Write(int)或Write(byte)?

Jon*_*eet 18

是的,它会这样做.这实际上是我的脑力激荡器#1.在通常使用的意义上,这不是真正的类型推断 - 它是重载决策.这就是你需要查看规范的地方.

现在,编译时类型Writer是Foo.

当你调用时writer.Write,编译器将以type开头,Foo并以类型为hiearchy的方式工作,直到它找到一个最初在该类型中声明的方法,它可以使用你给出的参数合法地调用它.一旦找到它,它就不会进一步升级.

现在,5可以转换为decimal(在它被专门转换为5之后也是如此byte) - 因此Foo.Write(decimal)是一个适用于您的方法调用的函数成员 - 这就是被调用的内容.它甚至没有考虑FooBase.Write重载,因为它已经找到匹配.

到目前为止,这是合理的 - 想法是向基类型添加方法不应该改变子类型不知道的现有代码的重载决策.当涉及到覆盖时,这会有所下降.让我们稍微改变你的代码 - 我将删除byte版本,make Write(int)virtual并覆盖它Foo:

public class FooBase
{
    public virtual void Write(int value)
    {
        //something
    }
}

public class Foo : FooBase
{
    public override void Write(int value)
    {
        //something
    }

    public void Write(decimal value)
    {
        //something
    }
}
Run Code Online (Sandbox Code Playgroud)

现在该怎么new Foo().Write(5)办?它仍然会调用Foo.Write(decimal)- 因为Foo.Write(int)没有在Foo中声明,只是在那里被覆盖.如果你override改为new那么它将被调用,因为那将被视为一个全新的方法声明.

我认为这方面是违反直觉的 - 并且它不需要进行版本控制,就像你在子类中重写一个方法一样,你清楚地知道它在基类中.

故事的寓意:尽量不要这样做.你最终会让人困惑.如果您派生自某个类,请不要添加具有相同名称但具有不同签名的新方法(如果您可以提供帮助).


mqp*_*mqp 7

你可以Write(byte)像这样打电话:

((FooBase)writer).Write((byte)6);
Run Code Online (Sandbox Code Playgroud)

通常,C#更喜欢直接使用类型上定义的重载并转换参数,而不是使用在父类型上定义的重载.

以这种方式使用方法遮蔽基类方法是一种糟糕的风格.


Eri*_*ert 6

这是一个经常被问到的问题.乔恩的分析当然是正确的.如需进一步阅读,请参阅我的文章:

http://blogs.msdn.com/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx