运行时类型与编译时类型方法调用

P.B*_*key 8 c# language-specifications

C#4.0规范如下:

调用虚方法时,进行该调用的实例的运行时类型决定了要调用的实际方法实现.在非虚方法调用中,实例的编译时类型是决定因素.

起初,我认为这与初始化有关.例如,给定两个初始化:

BaseClass bcDerived = new Derived(); VS BaseClass bcBase = new BaseClass();

和辅助类中的重载:

public virtual void Method(Derived d)
{
     Console.WriteLine("Result = derived called");
}

public virtual void Method(BaseClass d)
{
     Console.WriteLine("Result = base called");
}
Run Code Online (Sandbox Code Playgroud)

Methodvirtual在这种情况下,invokation不受关键字的影响.无论是否标记virtual,都会调用派生次数最少的重载.仅在overrideDerived类中,方法调用才会更改.

那么,"运行时类型"和"编译时类型"是什么意思呢?它们如何影响方法调用?

Ree*_*sey 6

这更多的是虚拟方法与非虚方法,以及调用的发生方式.您引用的规范部分处理变量上的方法调用 - 调用bcDerived.SomeMethod(),而不是调用foo.SomeMethod(bcDerived).

您引用的规范是指您具有非虚方法的情况:

public class A
{
    public void Foo() { Console.WriteLine("A.Foo"); }
    public virtual void Bar() { Console.WriteLine("A.Bar"); }
}
public class B : A
{
    public new void Foo() { Console.WriteLine("B.Foo"); }
    public override void Bar() { Console.WriteLine("B.Bar"); }
}
Run Code Online (Sandbox Code Playgroud)

然后调用的方法将由编译器在编译时确定,这样做:

A someInst = new B();
someInst.Foo();
Run Code Online (Sandbox Code Playgroud)

将导致此调用A.Foo(),不管是什么子类A的被提到了someInst,因为这是一个非虚方法.

但是,如果您有虚方法,则callvirt指令由编译器指定,编译器将决策移至运行时.这意味着:

 someInst.Bar();
Run Code Online (Sandbox Code Playgroud)

会打电话B.Bar(),而不是A.Bar().

在您的情况下,您不是在调用虚拟方法(在规范所指的意义上),而是在进行标准方法解析.C#规范的7.5.3详细介绍了过载分辨率.在您的情况下,参数list(bcDerived)由编译器检查,并被视为定义为类型BaseClass.对此的"最佳匹配"将是public virtual void Method(BaseClass d)参数列表直接匹配参数列表,因此在编译时使用.

如果查看规范,方法重载解析不直接使虚方法调用生效 - 它只查看类型之间的隐式转换.