非虚方法解析 - 为什么会发生这种情况

时间:2012-12-15 20:08:42

标签: c# polymorphism overload-resolution

我理解(在C#中)如何解析非虚方法是因为它取决于变量的类型(而不是实例的类型)。

看看下面的代码。

class Program
{
    static void Main(string[] args)
    {
        Sedan vehicle = new Sedan();
        vehicle.Drive();
        vehicle.Accelerate();
    }
}

abstract class VehicleBase
{
    public void Drive()
    {
        ShiftIntoGear();
        Accelerate();
        Steer();
    }

    protected abstract void ShiftIntoGear();
    protected abstract void Steer();

    public void Accelerate()
    {
        Console.WriteLine("VehicleBase.Accelerate");
    }
}

class Sedan : VehicleBase
{
    protected override void ShiftIntoGear()
    {
        Console.WriteLine("Sedan.ShiftIntoGear");
    }

    protected override void Steer()
    {
        Console.WriteLine("Sedan.Steer");
    }

    public new void Accelerate()
    {
        Console.WriteLine("Sedan.Accelerate");
    }
}

Console Windows显示以下内容:

Sedan.ShiftIntoGear
VehicleBase.Accelerate
Sedan.Steer
Sedan.Accelerate

这对我来说没有意义,我相信会让很多人为一个循环。如果您现在声明可变车辆属于VehicleBase类型,那么您将获得

Sedan.ShiftIntoGear
VehicleBase.Accelerate
Sedan.Steer
VehicleBase.Accelerate

这也是我在之前的案例中所期望的,因为Accelerate方法是非虚拟的。

在上一个输出中,(在变量车辆中输入轿车时,我希望调用Sedan.Accelerate而不是VehicleBase.Accelerate。就像现在一样,取决于你从哪里调用它(来自内部)这个阶级或外部的行为正在发生变化。

在我看来,重新引入方法的重载决策规则优先,但我很难相信这是正确/预期的行为。

2 个答案:

答案 0 :(得分:4)

所有这一切都非常有意义 - 当您将车辆声明为Sedan时,对Accelerate的两次调用将以不同方式解决:

  • 当使用Accelerate方法调用Drive时,它不知道new中有Sedan方法,因此会拨打电话到基础的相应方法
  • 当从Accelerate方法调用Main时,编译器知道您正在调用new方法,因为它知道{{1}的确切类型变量是vehicle

另一方面,当从Sedan方法调用Accelerate时,但该变量被声明为Main时,编译器不能假定类型为{ {1}},因此它再次将VehicleBase解析为基类的方法。

答案 1 :(得分:1)

在编译时根据表达式的类型解析非虚方法,并且在编译时修复调用目标。编译器在编译时在调用的源代码上下文中查找可用的符号信息中的方法名称,并发出调用指令以调用它能够找到的非虚方法。实例的实际类型无关紧要,调用始终转到编译时从静态类型信息中找出的非虚方法。

以下是Accelerate对Main的上下文中的Sedan.Accelerate的调用,但是VehicleBase.Accelerate的上下文中VehicleBase.Drive的调用:

Main函数的主体中,您已声明了Sedan类型的变量,并使用该变量进行方法调用。编译器在用于进行调用的变量类型中查找名为“Accelerate”的方法,键入Sedan,然后查找Sedan.Accelerate

在方法VehicleBase.Drive中,“自我”的编译时类型为VehicleBase。这是编译器在此源代码上下文中可以看到的唯一类型,因此在“VehicleBase.Drive”中对Accelerate的调用将始终转到VehicleBase.Accelerate,即使运行时对象实例的类型实际为Sedan

Sedan类型中声明的方法体中,编译器将首先查看Sedan类型的方法,然后查看{{}来解析非虚方法调用。 1}}如果在VehicleBase中找不到匹配项,则输入。

如果希望根据实际对象实例类型更改调用目标,则必须使用虚方法。使用虚方法还可以提供更加一致的执行结果,因为调用将始终转到对象实例在运行时确定的最具体的实现。

根据编译时类型选择非虚方法调用目标,而不了解运行时。在运行时根据实例类型选择虚方法调用目标。