`CallVirt`和`New`关键字

本文关键字:关键字 New CallVirt | 更新日期: 2023-09-27 18:27:47

此问题链接到当引用存储在基类变量中时CLR如何正确调用派生类隐藏的方法?

在我的情况下,我使用的是callvirt指令,而不是call

     class BaseClass
    {
        public void Write()
        {
            Method();
        }
        protected virtual void Method()
        {
            Console.WriteLine("Base - Method");
        }
    }
    class DerivedClass : BaseClass
    {
        private new void Method()
        {
            Console.WriteLine("Derived - Method");
        }
    }
    static void Main(string[] args)
    {
        DerivedClass dc = new DerivedClass();
        BaseClass bcdc = new DerivedClass();
        dc.Write();
        bcdc.Write();
        Console.ReadKey(true);
    }

输出:

 Base - Method
 Base - Method

Write方法的IL代码:

   .method public hidebysig instance void  Write() cil managed
   {
      // Code size       9 (0x9)
     .maxstack  8
     IL_0000:  nop
     IL_0001:  ldarg.0
     IL_0002:  callvirt   instance void Private_override.Program/BaseClass::Method()
     IL_0007:  nop
     IL_0008:  ret
   } // end of method BaseClass::Write

我不明白为什么要调用Base方法。

这里CLR使用callvirt指令,这意味着它将查找调用变量类型,由于类型是DerivedClass,而DerivedClass隐藏BaseClass.Method,因此堆中DerivedClass的MethodTable中应该只有DerivedClass.Method。为什么要调用BaseClass.Methodcallvirt在搜索方法时是否查找特定的override标志?

`CallVirt`和`New`关键字

因为您没有override。将new更改为override,它将按照您讨论的方式运行(您也必须更改protected的可访问性,否则它将无法编译)。new创建了一个不相关的方法,该方法恰好共享一个名称——它不属于基类中其他同名方法的多态性树的一部分。

你不妨问:"为什么这不叫CompletelyDifferentName()?"

class BaseClass
{
    public void Write()
    {
        Method();
    }
    protected virtual void Method()
    {
        Console.WriteLine("Base - Method");
    }
}
class DerivedClass : BaseClass
{
    private void CompletelyDifferentName()
    {
        Console.WriteLine("Derived - Method");
    }
}

答案是一样的:CompletelyDifferentName与称为Methodvirtual(多态)方法无关。new void Method()也不是——这就是new在这里的意思

因为这正是new的工作方式。New表示您的新方法和基类中同名的方法并没有任何共同点。这和用另一个名称在派生类中创建方法是一样的,只是使用相同的名称。也就是说,基类方法write不会尝试调用新的方法,因为它不是基类中方法的新版本,它只是该类的另一个成员。