假设我们有以下 C# 示例代码:
class BaseClass
{
public virtual void HelloWorld()
{
Console.WriteLine("Hello Tarik");
}
}
class DerivedClass : BaseClass
{
public override void HelloWorld()
{
base.HelloWorld();
}
}
class Program
{
static void Main(string[] args)
{
DerivedClass derived = new DerivedClass();
derived.HelloWorld();
}
}
当我编写以下代码时:
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
// Code size 15 (0xf)
.maxstack 1
.locals init ([0] class EnumReflection.DerivedClass derived)
IL_0000: nop
IL_0001: newobj instance void EnumReflection.DerivedClass::.ctor()
IL_0006: stloc.0
IL_0007: ldloc.0
IL_0008: callvirt instance void EnumReflection.BaseClass::HelloWorld()
IL_000d: nop
IL_000e: ret
} // end of method Program::Main
但是,csc.exe 转换derived.HelloWorld();
--> callvirt instance void EnumReflection.BaseClass::HelloWorld()
。这是为什么?我没有在任何地方提到 BaseClassMain
method.
而且如果它正在调用BaseClass::HelloWorld()
那么我会期望call
代替callvirt
因为它看起来直接调用BaseClass::HelloWorld()
method.
该调用转到 BaseClass::HelloWorld,因为 BaseClass 是定义该方法的类。虚拟调度在 C# 中的工作方式是在基类上调用该方法,并且虚拟调度系统负责确保调用该方法的最派生重写。
Eric Lippert 的这个回答内容非常丰富:https://stackoverflow.com/a/5308369/385844 https://stackoverflow.com/a/5308369/385844
正如他关于该主题的博客系列:http://blogs.msdn.com/b/ericlippert/archive/tags/virtual+dispatch/ http://blogs.msdn.com/b/ericlippert/archive/tags/virtual+dispatch/
您知道为什么要这样实现吗?如果直接调用派生类的ToString方法会发生什么?这样一来,乍一看我并没有太大的感觉……
它以这种方式实现,因为编译器不跟踪对象的运行时类型,只跟踪其引用的编译时类型。通过您发布的代码,很容易看出调用将转到该方法的 DerivedClass 实现。但假设derived
变量的初始化如下:
Derived derived = GetDerived();
有可能GetDerived()
返回一个实例StillMoreDerived
. If StillMoreDerived
(或之间的任何类别Derived
and StillMoreDerived
在继承链中)覆盖该方法,那么调用Derived
该方法的实施。
通过静态分析找到一个变量可以容纳的所有可能值就是解决停机问题。对于 .NET 程序集,问题更加严重,因为程序集可能不是完整的程序。因此,编译器可以合理证明的情况数量derived
不持有对更多派生对象的引用(或空引用)会很小。
添加这个逻辑以便它可以发出一个call
而不是callvirt
操作说明?毫无疑问,成本将远远高于所获得的微小收益。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)