方法解析顺序
假设我们有:
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 } }
比这个:
var writer = new Foo(); writer.Write(5); //calls Write(decimal) !! writer.Write((byte)6); //calls Write(decimal) !!
将调用Write(十进制)重载。 为什么? 我怎么能调用Write(int)或Write(byte)?
是的,它会这样做。 这实际上是我的脑力激荡器#1 。 在通常使用的意义上,这不是真正的类型推断 – 它是重载决策。 这就是你需要查看规范的地方。
现在, Writer
的编译时类型是Foo
。
当你调用writer.Write
,编译器将以类型Foo
开始并向上运行类型为hiearchy,直到它找到一个最初在该类型中声明的方法,它可以使用你给出的参数合法地调用它。 一旦找到它,它就不会进一步向上层次结构。
现在,5可转换为decimal
(因此在将其专门转换为byte
后为5) – 所以Foo.Write(decimal)
是方法调用的适用函数成员 – 这就是被调用的内容。 它甚至不考虑FooBase.Write
重载,因为它已经找到匹配。
到目前为止,这是合理的 – 想法是向基类型添加方法不应该更改子类型不知道的现有代码的重载决策。 当涉及到覆盖时,这会有所下降。 让我们稍微改变你的代码 – 我将删除byte
版本,使Write(int)
虚拟并在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 } }
现在new Foo().Write(5)
怎么做? 它仍将调用Foo.Write(decimal)
– 因为Foo.Write(int)
未在Foo中声明 ,只在那里被覆盖 。 如果将override
更改为new
,则会调用它,因为它将被视为全新的方法声明。
我认为这方面是违反直觉的 – 并且它不需要进行版本控制,就像你在子类中重写一个方法一样,你清楚地知道它在基类中。
故事的寓意 :尽量不要这样做。 你最终会让人困惑。 如果您派生自某个类,请不要添加具有相同名称但具有不同签名的新方法(如果您可以提供帮助)。
您可以像这样调用Write(byte)
:
((FooBase)writer).Write((byte)6);
通常,C#更喜欢直接使用类型上定义的重载并转换参数,而不是使用在父类型上定义的重载。
以这种方式使用方法遮蔽基类方法是一种糟糕的风格。
这是一个经常被问到的问题。 乔恩的分析当然是正确的。 如需进一步阅读,请参阅我的文章:
http://blogs.msdn.com/ericlippert/archive/2007/09/04/future-breaking-changes-part-three.aspx