我刚刚读到这个答案
Which overload is called and how?
由Jon Skeet和我刚刚完成了解如何在编译时完成重载决策 - 这怎么可能?在你跑之前你不知道对象的类型吗?我一直认为所有方法调用都是在运行时完成的(后期绑定)
有什么例外?
我举一个例子:
public void DoWork(IFoo)
public void DoWork(Bar)
IFoo a = new Bar();
DoWork(a)
这里调用哪种方法以及为什么?
答案 0 :(得分:3)
我认为您将重载决议与virtual method dispatch混淆。是的,对象的运行时类型将确定要运行的方法,但该方法已被编译器绑定。这是一种类型安全的方式,允许多态行为,而没有真正的后期绑定的所有灵活性和危险。
编译器将根据代码中编写的参数类型确定调用哪个方法重载。
答案 1 :(得分:2)
虚拟方法(包括通过接口调用)在运行时调度接收器的类型(后期绑定)。
使用引用的编译时类型在编译时解析非虚方法。这就是为什么阴影(new
修饰符)会导致不同的行为,具体取决于您是通过基类引用还是派生类引用调用阴影方法。
在所有情况下,重载解析都使用参数的编译时类型。只有接收者 - 即x
形式的调用中的x.SomeMethod(y, z)
被认为是后期绑定。因此,如果y
被输入为对象,并且唯一的重载是string y
和int y
,则编译器将会出错,即使在运行时y
实际上是一个字符串或int - 因为它只考虑编译时类型(声明的变量类型)。
答案 2 :(得分:2)
当编译器遇到方法调用时,所有参数的类型都是已知的,因为C#是一种静态类型的语言:所有表达式和变量都是特定类型,并且该类型是明确的并且在编译时是已知的。< / p>
这忽略了dynamic
,这使事情稍微复杂化。
编辑:这是对您的修改的回复。为清楚起见,我将您的代码翻译成以下内容:
interface IFoo { }
class Bar : IFoo { }
class Test {
public void DoWork(IFoo a) { }
public void DoWork(Bar b) { }
}
class Program {
static void Main(string[] args) {
IFoo a = new Bar();
Test t = new Test();
t.DoWork(a);
}
}
在Test.DoWork(IFoo)
中调用Test.DoWork(Bar)
时,您询问此处调用的方法(t.DoWork(a)
或Main
)。答案是Test.DoWork(IFoo)
被调用。这基本上是因为参数被输入为IFoo
。我们按照规范(§7.4.3.1):
当满足以下所有条件时,函数成员被称为关于参数列表A的适用函数成员:
A中的参数数量与函数成员声明中的参数数量相同。
对于A中的每个参数,参数的参数传递模式(即value,ref或out)与相应参数的参数传递模式相同,并且
对于值参数或参数数组,,从参数到相应参数的类型存在隐式转换(第6.1节),或
对于ref或out参数,参数的类型与相应参数的类型相同。毕竟,ref或out参数是传递的参数的别名。
这里的问题(参见粗体声明)是没有从IFoo
到Bar
的隐式转换。因此,方法Test.DoWork(Bar)
不是适用的功能成员。显然Test.DoWork(IFoo)
是一个适用的函数成员,作为唯一选择,编译器将选择它作为调用方法。
答案 3 :(得分:1)
在运行时唯一决定的是“虚拟”功能,你可以为你的对象调用适当的函数。