《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的例子
示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 | // 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,这里记录下来方便以后查阅。
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 智能桌面机器人:用.NET IoT库控制舵机并多方法播放表情
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 新年开篇:在本地部署DeepSeek大模型实现联网增强的AI应用
· DeepSeek火爆全网,官网宕机?本地部署一个随便玩「LLM探索」
· Janus Pro:DeepSeek 开源革新,多模态 AI 的未来
· 上周热点回顾(1.20-1.26)
· 【译】.NET 升级助手现在支持升级到集中式包管理