当我们写C#代码或者学习C#时,我们会经常使用到构造函数,构造函数是用于初始化类的实例的特殊函数。我们可以使用IL工具来看一下C#编译后的构造函数生成的IL代码,来加深对C#构造函数的理解。下面我们来介绍如何通过IL了解C#类的构造函数。
工具准备
首先,我们需要安装ILSpy工具,使用这个工具可以打开编译好的C#程序集,并且可以查看程序集的IL代码。
ILSpy下载地址:https://github.com/icsharpcode/ilspy/releases
步骤说明
- 创建一个简单的C#类,定义一个构造函数,例如:
public class MyClass
{
public MyClass()
{
Console.WriteLine("Constructor called.");
}
}
-
编译上面的类得到.exe或.dll文件。
-
打开ILSpy工具,点击“文件”->“打开文件”,选择编译好的程序集,打开后可以看到程序集的各个类、方法等信息。
-
找到刚才创建的MyClass类,双击进入该类的源代码窗口。
-
在该类的源代码窗口中,点击“语法树”选项卡,可以查看该类的抽象语法树(AST)。
-
在该类的源代码窗口中,点击“IL”选项卡,可以查看该类编译好后的IL代码。
在IL代码中,我们可以找到构造函数的方法体,一般以“.ctor”为方法名的形式,例如:“.ctor()”。
下面是一个简单的示例IL代码:
.class public auto ansi beforefieldinit MyClass
extends [mscorlib]System.Object
{
.method public hidebysig specialname rtspecialname
instance void .ctor() cil managed
{
.maxstack 8
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ldstr "Constructor called."
IL_000c: call void [mscorlib]System.Console::WriteLine(string)
IL_0011: nop
IL_0012: ret
}
}
在这个示例中,我们看到了“.ctor()”方法的IL代码,其中包括了类似于“ldarg.0”、“call instance void [mscorlib]System.Object::.ctor()”等指令,这些指令表明了C#编译器对构造函数具体实现的细节。
通过查看构造函数的IL代码,我们可以深入了解C#构造函数的生成和调用过程,从而更好地理解C#的类和对象的创建过程。
示例说明
下面给出两个示例来进一步说明如何通过IL了解C#类的构造函数。
示例一
public class MyClass
{
private int num;
public MyClass(int n)
{
num = n;
}
public void PrintNum()
{
Console.WriteLine("Num: " + num);
}
}
通过ILSpy查看生成的IL代码:
.class public auto ansi beforefieldinit MyClass
extends [mscorlib]System.Object
{
.field private int32 num
.method public hidebysig specialname rtspecialname
instance void .ctor(
int32 n
) cil managed
{
.maxstack 8
/* 省略了一个调用 Object 的默认构造函数的指令*/
IL_0009: ldarg.0
IL_000a: ldarg.1
IL_000b: stfld int32 MyClass::num
IL_0010: nop
IL_0011: ret
}
.method public hidebysig
instance void PrintNum () cil managed
{
.maxstack 8
IL_0000: ldstr "Num: "
IL_0005: ldarg.0
IL_0006: ldfld int32 MyClass::num
IL_000b: box int32
IL_0010: call string [mscorlib]System.String::Concat(object, object)
IL_0015: call void [mscorlib]System.Console::WriteLine(string)
IL_001a: nop
IL_001b: ret
}
}
可以看到类的构造方法生成的方法头现实了参数列表 n ,调用方法还省略了调用 Object 的默认构造函数的指令,而使用 ldarg 将 this 引用压入了堆栈,并加载 n 并压入了堆栈,最后使用 stfld 将值存回成员变量 num 中。
示例二
public class MyClass
{
// 静态字段
public static int count = 0;
// 实例字段
private int num;
public MyClass(int n)
{
count++;
num = n;
}
public void PrintNum()
{
Console.WriteLine("Num: " + num);
}
public void PrintCount()
{
Console.WriteLine("Count: " + count);
}
}
通过ILSpy查看生成的IL代码:
.class public auto ansi beforefieldinit MyClass
extends [mscorlib]System.Object
{
.field public static int32 count
.field private int32 num
.method public hidebysig specialname rtspecialname
instance void .ctor(
int32 n
) cil managed
{
.maxstack 8
/* 调用 Object 的默认构造函数的指令*/
IL_0000: ldarg.0
IL_0001: call instance void [mscorlib]System.Object::.ctor()
IL_0006: nop
IL_0007: ldc.i4.1
IL_0008: call class [mscorlib]System.Threading.Interlocked [mscorlib]System.Threading.Interlocked::Increment(int32&)
IL_000d: pop
IL_000e: ldarg.0
IL_000f: ldarg.1
IL_0010: stfld int32 MyClass::num
IL_0015: nop
IL_0016: ret
}
.method public hidebysig
instance void PrintNum () cil managed
{
.maxstack 8
IL_0000: ldstr "Num: "
IL_0005: ldarg.0
IL_0006: ldfld int32 MyClass::num
IL_000b: box int32
IL_0010: call string [mscorlib]System.String::Concat(object, object)
IL_0015: call void [mscorlib]System.Console::WriteLine(string)
IL_001a: nop
IL_001b: ret
}
.method public hidebysig
instance void PrintCount () cil managed
{
.maxstack 8
IL_0000: ldstr "Count: "
IL_0005: call class [mscorlib]System.Type [mscorlib]System.Type::GetTypeFromHandle(valuetype [mscorlib]System.RuntimeTypeHandle)
IL_000a: call string [mscorlib]System.Runtime.CompilerServices.RuntimeHelpers::get_FullName(class [mscorlib]System.Type)
IL_000f: call string [mscorlib]System.String::Concat(object, object)
IL_0014: call void [mscorlib]System.Console::WriteLine(string)
IL_0019: nop
IL_001a: ret
}
}
可以看到在构造函数内部通过 Interlocked.Increment 原子的将静态字段 count 计数器加 1 。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:如何通过IL了解C#类的构造函数浅析 - Python技术站