在编译时完成重载

时间:2010-01-10 19:50:13

标签: c#

我刚刚读到这个答案

Which overload is called and how?

由Jon Skeet和我刚刚完成了解如何在编译时完成重载决策 - 这怎么可能?在你跑之前你不知道对象的类型吗?

我一直认为所有方法调用都是在运行时完成的(后期绑定)

有什么例外?

我举一个例子:

public void DoWork(IFoo)
public void DoWork(Bar) 

IFoo a = new Bar();
DoWork(a)

这里调用哪种方法以及为什么?

4 个答案:

答案 0 :(得分:3)

我认为您将重载决议与virtual method dispatch混淆。是的,对象的运行时类型将确定要运行的方法,但该方法已被编译器绑定。这是一种类型安全的方式,允许多态行为,而没有真正的后期绑定的所有灵活性和危险。

编译器将根据代码中编写的参数类型确定调用哪个方法重载。

答案 1 :(得分:2)

虚拟方法(包括通过接口调用)在运行时调度接收器的类型(后期绑定)。

使用引用的编译时类型在编译时解析非虚方法。这就是为什么阴影(new修饰符)会导致不同的行为,具体取决于您是通过基类引用还是派生类引用调用阴影方法。

在所有情况下,重载解析都使用参数的编译时类型。只有接收者 - 即x形式的调用中的x.SomeMethod(y, z)被认为是后期绑定。因此,如果y被输入为对象,并且唯一的重载是string yint 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参数是传递的参数的别名。

这里的问题(参见粗体声明)是没有从IFooBar的隐式转换。因此,方法Test.DoWork(Bar)不是适用的功能成员。显然Test.DoWork(IFoo)是一个适用的函数成员,作为唯一选择,编译器将选择它作为调用方法。

答案 3 :(得分:1)

在运行时唯一决定的是“虚拟”功能,你可以为你的对象调用适当的函数。