call、callvirt和calli指令用于完成方法调用,有何区别呢?
1)call使用静态调度,也就是根据引用类型的静态类型来调度方法。call指令根据引用变量的类型来调用方法,因此通常用于调用非虚方法。
2)callvirt使用虚拟调度,也就是根据引用类型的动态类型来调度方法;callvirt指令根据引用变量指向的对象类型来调用方法,执行时会递归的调用给自己知道堆栈溢出,从而实现了在运行时的动态绑定,因此通常用于调用虚方法。
3)calli又称间接调用,是通过函数指针来执行方法调用的。与call和callvirt相对应的(直接调用)
Father类
public class Father { public void DoWork() { Console.WriteLine("Father.DoWork()"); } public virtual void DoVirtualWork() { Console.WriteLine("Father.DoVirtualWork()"); } public virtual void DoVirtualAll() { Console.WriteLine("Father.DoVirtualAll()"); } }
Son类
public class Son:Father { public static void DoStaticWork() { Console.WriteLine("Son.DoStaticWork()"); } public new void DoWork()//new表示对父类的阻断 { Console.WriteLine("Son.DoWork()"); } public new virtual void DoVirtualWork() { Console.WriteLine("Son.DoVirtualWork()"); } public override void DoVirtualAll() { base.DoVirtualAll(); Console.WriteLine("Son.DoVirtualAll()"); } }
GrandSon类
public class GrandSon:Son { public override void DoVirtualWork() { base.DoVirtualWork(); Console.WriteLine("GrnadSon.DoVirtualWork()"); } public override void DoVirtualAll() { base.DoVirtualAll(); Console.WriteLine("GrandSon.DoVirtualAll()"); } }
Prgram.cs
class Program { static void Main(string[] args) { Father son = new Son(); son.DoWork(); son.DoVirtualWork(); Son.DoStaticWork(); Father aGrandSon = new GrandSon(); aGrandSon.DoWork(); aGrandSon.DoVirtualWork(); aGrandSon.DoVirtualAll(); Console.ReadLine(); } }
输出结果
IL代码
.method private hidebysig static void Main(string[] args) cil managed { .entrypoint // 代码大小 61 (0x3d) .maxstack 1 .locals init ([0] class ConsoleApplication1.Father son, [1] class ConsoleApplication1.Father aGrandSon) IL_0000: nop IL_0001: newobj instance void ConsoleApplication1.Son::.ctor() IL_0006: stloc.0 IL_0007: ldloc.0 IL_0008: callvirt instance void ConsoleApplication1.Father::DoWork() IL_000d: nop IL_000e: ldloc.0 IL_000f: callvirt instance void ConsoleApplication1.Father::DoVirtualWork() IL_0014: nop IL_0015: call void ConsoleApplication1.Son::DoStaticWork() IL_001a: nop IL_001b: newobj instance void ConsoleApplication1.GrandSon::.ctor() IL_0020: stloc.1 IL_0021: ldloc.1 IL_0022: callvirt instance void ConsoleApplication1.Father::DoWork() IL_0027: nop IL_0028: ldloc.1 IL_0029: callvirt instance void ConsoleApplication1.Father::DoVirtualWork() IL_002e: nop IL_002f: ldloc.1 IL_0030: callvirt instance void ConsoleApplication1.Father::DoVirtualAll() IL_0035: nop IL_0036: call string [mscorlib]System.Console::ReadLine() IL_003b: pop IL_003c: ret } // end of method Program::Main