一、观察Lambda函数编译生成IL代码。
1. 源代码
internal class Program
{
static void Main(string[] args)
{
Func<int, int, int> add = (a, b) => a + b;
add(1, 2);
}
}
2. IL代码
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Header size: 12
// Code size: 43 (0x2b)
.maxstack 3
.entrypoint
.locals init (
[0] class [mscorlib]System.Func`3<int32, int32, int32> 'add'
)
// {
IL_0000: nop
// Func<int, int, int> func = (int a, int b) => a + b;
IL_0001: ldsfld class [mscorlib]System.Func`3<int32, int32, int32> ConsoleApp5.Program/'<>c'::'<>9__0_0'
IL_0006: dup
IL_0007: brtrue.s IL_0020
// (no C# code)
IL_0009: pop
// func(1, 2);
IL_000a: ldsfld class ConsoleApp5.Program/'<>c' ConsoleApp5.Program/'<>c'::'<>9'
IL_000f: ldftn instance int32 ConsoleApp5.Program/'<>c'::'<Main>b__0_0'(int32, int32)
IL_0015: newobj instance void class [mscorlib]System.Func`3<int32, int32, int32>::.ctor(object, native int)
IL_001a: dup
IL_001b: stsfld class [mscorlib]System.Func`3<int32, int32, int32> ConsoleApp5.Program/'<>c'::'<>9__0_0'
IL_0020: stloc.0
IL_0021: ldloc.0
IL_0022: ldc.i4.1
IL_0023: ldc.i4.2
IL_0024: callvirt instance !2 class [mscorlib]System.Func`3<int32, int32, int32>::Invoke(!0, !1)
IL_0029: pop
// }
IL_002a: ret
} // end of method Program::Main
.class nested private auto ansi sealed serializable beforefieldinit '<>c'
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Fields
.field public static initonly class ConsoleApp5.Program/'<>c' '<>9'
.field public static class [mscorlib]System.Func`3<int32, int32, int32> '<>9__0_0'
// Methods
.method private hidebysig specialname rtspecialname static
void .cctor () cil managed
{
// Method begins at RVA 0x2090
// Header size: 1
// Code size: 11 (0xb)
.maxstack 8
// <>9 = new <>c();
IL_0000: newobj instance void ConsoleApp5.Program/'<>c'::.ctor()
IL_0005: stsfld class ConsoleApp5.Program/'<>c' ConsoleApp5.Program/'<>c'::'<>9'
// }
IL_000a: ret
} // end of method '<>c'::.cctor
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x209c
// Header size: 1
// Code size: 8 (0x8)
.maxstack 8
// {
IL_0000: ldarg.0
// (no C# code)
IL_0001: call instance void [mscorlib]System.Object::.ctor()
// }
IL_0006: nop
IL_0007: ret
} // end of method '<>c'::.ctor
.method assembly hidebysig
instance int32 '<Main>b__0_0' (
int32 a,
int32 b
) cil managed
{
// Method begins at RVA 0x20a5
// Header size: 1
// Code size: 4 (0x4)
.maxstack 8
// return a + b;
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: add
IL_0003: ret
} // end of method '<>c'::'<Main>b__0_0'
} // end of class <>c
执行步骤:
-
初始化时,生成一个名称为
add
的Func<int, int, int>
委托的局部变量,序号为0,初始值为null
。 -
IL_0001~IL_0007: 检查静态字段
<>9__0_0
是否已缓存委托实例,避免重复创建。如果已创建,则直接跳转到调用部分。 -
IL_0009~IL_001b: 加载lambda编译后的方法地址:
<Main>b__0_0
(即a + b
逻辑),并创建Func<int,int,int>
委托实例。 -
IL_0020:
stloc.0
将委托存入序号为0的局部变量。 -
IL_0021:
ldloc.0
读取序号为0的局部变量,即Func<int, int, int>
委托。 -
IL_0022~IL_0024: 压入参数a, b,即1和2,并调用
add
委托。
3. 总结
lambda委托在编译后,会生成一个class,来承载对应的委托方法。内部使用静态字段来缓存委托,减少多次调用性能损耗。
二、观察lambda函数闭包编译生成的IL代码。
1. 源代码
internal class Program
{
static void Main(string[] args)
{
int c = 1;
Func<int, int, int> add = (a, b) => a + b + c;
c = 2;
add(1, 2);
}
}
运行结果:5
2. IL代码
.method private hidebysig static
void Main (
string[] args
) cil managed
{
// Method begins at RVA 0x2050
// Header size: 12
// Code size: 44 (0x2c)
.maxstack 3
.entrypoint
.locals init (
[0] class ConsoleApp5.Program/'<>c__DisplayClass0_0' 'CS$<>8__locals0',
[1] class [mscorlib]System.Func`3<int32, int32, int32> 'add'
)
// {
IL_0000: newobj instance void ConsoleApp5.Program/'<>c__DisplayClass0_0'::.ctor()
IL_0005: stloc.0
// (no C# code)
IL_0006: nop
// int c = 1;
IL_0007: ldloc.0
IL_0008: ldc.i4.1
IL_0009: stfld int32 ConsoleApp5.Program/'<>c__DisplayClass0_0'::c
// Func<int, int, int> func = (int a, int b) => a + b + c;
IL_000e: ldloc.0
IL_000f: ldftn instance int32 ConsoleApp5.Program/'<>c__DisplayClass0_0'::'<Main>b__0'(int32, int32)
IL_0015: newobj instance void class [mscorlib]System.Func`3<int32, int32, int32>::.ctor(object, native int)
IL_001a: stloc.1
// c = 2;
IL_001b: ldloc.0
IL_001c: ldc.i4.2
IL_001d: stfld int32 ConsoleApp5.Program/'<>c__DisplayClass0_0'::c
// func(1, 2);
IL_0022: ldloc.1
IL_0023: ldc.i4.1
IL_0024: ldc.i4.2
IL_0025: callvirt instance !2 class [mscorlib]System.Func`3<int32, int32, int32>::Invoke(!0, !1)
IL_002a: pop
// }
IL_002b: ret
} // end of method Program::Main
.class nested private auto ansi sealed beforefieldinit '<>c__DisplayClass0_0'
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = (
01 00 00 00
)
// Fields
.field public int32 c
// Methods
.method public hidebysig specialname rtspecialname
instance void .ctor () cil managed
{
// Method begins at RVA 0x2091
// Header size: 1
// Code size: 8 (0x8)
.maxstack 8
// {
IL_0000: ldarg.0
// (no C# code)
IL_0001: call instance void [mscorlib]System.Object::.ctor()
// }
IL_0006: nop
IL_0007: ret
} // end of method '<>c__DisplayClass0_0'::.ctor
.method assembly hidebysig
instance int32 '<Main>b__0' (
int32 a,
int32 b
) cil managed
{
// Method begins at RVA 0x209a
// Header size: 1
// Code size: 11 (0xb)
.maxstack 8
// return a + b + c;
IL_0000: ldarg.1
IL_0001: ldarg.2
IL_0002: add
IL_0003: ldarg.0
IL_0004: ldfld int32 ConsoleApp5.Program/'<>c__DisplayClass0_0'::c
IL_0009: add
IL_000a: ret
} // end of method '<>c__DisplayClass0_0'::'<Main>b__0'
} // end of class <>c__DisplayClass0_0
执行步骤与之前类似,主要有几点区别:
-
生成的委托类
'<>c__DisplayClass0_0'
中,变量c
作为字段被存储在类中。 -
IL_0007~IL_0009:
int c = 1;
将数字1
存储在局部变量(委托实例)'CS$<>8__locals0'
的字段c
中。 -
IL_001b~IL_001d:
c = 2;
将数字2
存储在局部变量(委托实例)'CS$<>8__locals0'
的字段c
中。 -
使用类中字段
c
参与函数add计算。
3. 总结
局部变量闭包,会作为委托实例的字段,随实例一同保存。局部变量变更,即同时变更委托实例字段中值。