《CLR Via C# 第3版》笔记之(六) - IL中的call和callvirt
C#中调用一个函数时生成的IL代码有两种形式,分别为call 和 callvirt。
主要内容
- call和callvirt的区别
- call和callvirt的例子
1. call和callvirt的区别
call的callvirt的区别主要有两点:
1)call可以调用静态方法,实例方法和虚方法
callvirt只能调用实例方法和虚方法,不能调用静态方法
2)call一般是以非虚的方式来调用函数的
callvirt是以已多态的方式来调用函数的
2. call和callvirt的例子
示例代码如下:
using System; namespace Test6 { public class CLRviaCSharp_6 { static void Main(string[] args) { BaseClass.SShow(); BaseClass b = new BaseClass(); BaseClass s = new SubClass(); b.VShow(); s.VShow(); Console.ReadKey(true); } } public class BaseClass { public static void SShow() { Console.WriteLine("Base class static method: SShow()!"); } public virtual void VShow() { Console.WriteLine("Base class virtual method: VShow()!"); } } public class SubClass : BaseClass { public override void VShow() { Console.WriteLine("Sub class virtual method: VShow()!"); } } }
程序执行结果为:
利用ildasm.exe将上面代码生成的exe文件反编译为IL代码。
命令:ildasm .\Test6.exe /output:Test6.il
IL代码如下:
// Microsoft (R) .NET Framework IL Disassembler. Version 3.5.30729.1 // Copyright (c) Microsoft Corporation. All rights reserved. // Metadata version: v4.0.30319 .assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 4:0:0:0 } .assembly Test6 { .custom instance void [mscorlib]System.Reflection.AssemblyTitleAttribute::.ctor(string) = ( 01 00 0C 63 6E 62 6C 6F 67 5F 62 6F 77 65 6E 00 // ...cnblog_bowen. 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyDescriptionAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyConfigurationAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyCompanyAttribute::.ctor(string) = ( 01 00 18 4C 65 6E 6F 76 6F 20 28 42 65 69 6A 69 // ...Lenovo (Beiji 6E 67 29 20 4C 69 6D 69 74 65 64 00 00 ) // ng) Limited.. .custom instance void [mscorlib]System.Reflection.AssemblyProductAttribute::.ctor(string) = ( 01 00 0C 63 6E 62 6C 6F 67 5F 62 6F 77 65 6E 00 // ...cnblog_bowen. 00 ) .custom instance void [mscorlib]System.Reflection.AssemblyCopyrightAttribute::.ctor(string) = ( 01 00 2A 43 6F 70 79 72 69 67 68 74 20 C2 A9 20 // ..*Copyright .. 4C 65 6E 6F 76 6F 20 28 42 65 69 6A 69 6E 67 29 // Lenovo (Beijing) 20 4C 69 6D 69 74 65 64 20 32 30 31 30 00 00 ) // Limited 2010.. .custom instance void [mscorlib]System.Reflection.AssemblyTrademarkAttribute::.ctor(string) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.InteropServices.ComVisibleAttribute::.ctor(bool) = ( 01 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.InteropServices.GuidAttribute::.ctor(string) = ( 01 00 24 61 64 61 62 34 66 63 33 2D 33 33 64 35 // ..$adab4fc3-33d5 2D 34 62 64 30 2D 39 61 32 61 2D 39 35 35 61 65 // -4bd0-9a2a-955ae 66 38 35 33 31 62 37 00 00 ) // f8531b7.. .custom instance void [mscorlib]System.Reflection.AssemblyFileVersionAttribute::.ctor(string) = ( 01 00 07 31 2E 30 2E 30 2E 30 00 00 ) // ...1.0.0.0.. .custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 29 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B // ..).NETFramework 2C 56 65 72 73 69 6F 6E 3D 76 34 2E 30 2C 50 72 // ,Version=v4.0,Pr 6F 66 69 6C 65 3D 43 6C 69 65 6E 74 01 00 54 0E // ofile=Client..T. 14 46 72 61 6D 65 77 6F 72 6B 44 69 73 70 6C 61 // .FrameworkDispla 79 4E 61 6D 65 1F 2E 4E 45 54 20 46 72 61 6D 65 // yName..NET Frame 77 6F 72 6B 20 34 20 43 6C 69 65 6E 74 20 50 72 // work 4 Client Pr 6F 66 69 6C 65 ) // ofile // --- The following custom attribute is added automatically, do not uncomment ------- // .custom instance void [mscorlib]System.Diagnostics.DebuggableAttribute::.ctor(valuetype [mscorlib]System.Diagnostics.DebuggableAttribute/DebuggingModes) = ( 01 00 07 01 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilationRelaxationsAttribute::.ctor(int32) = ( 01 00 08 00 00 00 00 00 ) .custom instance void [mscorlib]System.Runtime.CompilerServices.RuntimeCompatibilityAttribute::.ctor() = ( 01 00 01 00 54 02 16 57 72 61 70 4E 6F 6E 45 78 // ....T..WrapNonEx 63 65 70 74 69 6F 6E 54 68 72 6F 77 73 01 ) // ceptionThrows. .hash algorithm 0x00008004 .ver 1:0:0:0 } .module Test6.exe // MVID: {7020C5F0-2D89-4214-AD2C-E6BFBEC57CA1} .imagebase 0x00400000 .file alignment 0x00000200 .stackreserve 0x00100000 .subsystem 0x0003 // WINDOWS_CUI .corflags 0x00000003 // ILONLY 32BITREQUIRED // Image base: 0x030F0000 // =============== CLASS MEMBERS DECLARATION =================== .class public auto ansi beforefieldinit Test6.CLRviaCSharp_6 extends [mscorlib]System.Object { .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 41 (0x29) .maxstack 1 .locals init ([0] class Test6.BaseClass b, [1] class Test6.BaseClass s) IL_0000: nop IL_0001: call void Test6.BaseClass::SShow() IL_0006: nop IL_0007: newobj instance void Test6.BaseClass::.ctor() IL_000c: stloc.0 IL_000d: newobj instance void Test6.SubClass::.ctor() IL_0012: stloc.1 IL_0013: ldloc.0 IL_0014: callvirt instance void Test6.BaseClass::VShow() IL_0019: nop IL_001a: ldloc.1 IL_001b: callvirt instance void Test6.BaseClass::VShow() IL_0020: nop IL_0021: ldc.i4.1 IL_0022: call valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey(bool) IL_0027: pop IL_0028: ret } // end of method CLRviaCSharp_6::Main .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method CLRviaCSharp_6::.ctor } // end of class Test6.CLRviaCSharp_6 .class public auto ansi beforefieldinit Test6.BaseClass extends [mscorlib]System.Object { .method public hidebysig static void SShow() cil managed { // Code size 13 (0xd) .maxstack 8 IL_0000: nop IL_0001: ldstr "Base class static method: SShow()!" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop IL_000c: ret } // end of method BaseClass::SShow .method public hidebysig newslot virtual instance void VShow() cil managed { // Code size 13 (0xd) .maxstack 8 IL_0000: nop IL_0001: ldstr "Base class virtual method: VShow()!" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop IL_000c: ret } // end of method BaseClass::VShow .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void [mscorlib]System.Object::.ctor() IL_0006: ret } // end of method BaseClass::.ctor } // end of class Test6.BaseClass .class public auto ansi beforefieldinit Test6.SubClass extends Test6.BaseClass { .method public hidebysig virtual instance void VShow() cil managed { // Code size 13 (0xd) .maxstack 8 IL_0000: nop IL_0001: ldstr "Sub class virtual method: VShow()!" IL_0006: call void [mscorlib]System.Console::WriteLine(string) IL_000b: nop IL_000c: ret } // end of method SubClass::VShow .method public hidebysig specialname rtspecialname instance void .ctor() cil managed { // Code size 7 (0x7) .maxstack 8 IL_0000: ldarg.0 IL_0001: call instance void Test6.BaseClass::.ctor() IL_0006: ret } // end of method SubClass::.ctor } // end of class Test6.SubClass // ============================================================= // *********** DISASSEMBLY COMPLETE *********************** // WARNING: Created Win32 resource file Test6.res
其中Main函数从63行开始。
第71行的静态方法由call来调用的,78行和81行的虚方法则是由callvirt来调用的。
为了验证call使用非虚的方式来调用方法的,我们将生成的IL文件Test6.il中81行的callvirt改为call。
修改Test6.il后保存,然后用ilasm.exe来编译此IL文件为新的exe文件。
命令:ilasm .\Test6.il /res:Test6.res /output:Test6_new.exe
然后执行Test6_new.exe,发现最后一步也是调用基类的方法,无法表现出多态性。
其实我们在写C#代码时并不用关心call和callvirt,c#编译器会帮助我们选择合适的调用方法。
只是在分析IL时经常会遇到callvirt,这里记录下来方便以后查阅。