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,这里记录下来方便以后查阅。
本文转自wang_yb博客园博客,原文链接:http://www.cnblogs.com/wang_yb/archive/2011/06/28/2092327.html,如需转载请自行联系原作者