【转】编写高质量代码改善C#程序的157个建议——建议38:小心闭包中的陷阱
2021-04-24 15:28
标签:virt col eric manage cal write 编译 nop temp 建议38:小心闭包中的陷阱 先看一下下面的代码,设想一下输出的是什么? 我们的设计意图是让匿名方法(在这里表现为Lambda表达式)接受参数 i ,并输出: 0 1 2 3 4 而实际上输出为: 5 5 5 5 5 这段代码并不像我们想象的那么简单,要完全理解运行时代码是怎么运行的,首先必须理解C#编译器为我们做了什么。 IL代码如下: //以下省略 在L_0009行,发现编译器为我们创建了一个类“c__DisplayClass2”,并且在循环内部每次会为这个类的一个实例变量 i 赋值。 这个类的IL代码为: 经过分析,会发现前面的这段代码实际和下面这段代码是一致的: 这段代码演示的就是闭包对象。所谓闭包对象,指的是上面这种情形中的TempClass对象(在第一段代码中,就是编译器为我们生成的c__DisplayClass2对象)。如果匿名方法(lambda表达式)引用了某个局部变量,编译器就会自动将该引用提升到闭包对象中,即将for循环中的变量 i 修改成了引用闭包对象的公共变量 i 。这样,即使代码执行离开了原局部变量 i 的作用域(如for循环),包含该闭包对象的作用域还存在。理解了这一点,就理解了代码的输出了。 要实现本建议开始时所预期的输出,可以将闭包对象的产生放在for循环内部: 此代码和下面的代码一致: 转自:《编写高质量代码改善C#程序的157个建议》陆敏技 【转】编写高质量代码改善C#程序的157个建议——建议38:小心闭包中的陷阱 标签:virt col eric manage cal write 编译 nop temp 原文地址:http://www.cnblogs.com/farmer-y/p/7943739.html static void Main(string[] args)
{
List lists = new List();
for (int i = 0; i 5; i++)
{
Action t = () =>
{
Console.WriteLine(i.ToString());
};
lists.Add(t);
}
foreach (Action t in lists)
{
t();
}
}
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint
.maxstack 3
.locals init (
[0] class [mscorlib]System.Collections.Generic.List`1class [mscorlib]System.Action> lists,
[1] class [mscorlib]System.Action t,
[2] class [mscorlib]System.Action CS$9__CachedAnonymousMethodDelegate1,
[3] class MyTest.Program/c__DisplayClass2 CS$8__locals3,
[4] bool CS$4$0000,
[5] valuetype [mscorlib]System.Collections.Generic.List`1/Enumeratorclass [mscorlib]System.Action> CS$5$0001)
L_0000: nop
L_0001: newobj instance void [mscorlib]System.Collections.Generic.List`1class [mscorlib]System.Action>::.ctor()
L_0006: stloc.0
L_0007: ldnull
L_0008: stloc.2
L_0009: newobj instance void MyTest.Program/c__DisplayClass2::.ctor()
L_000e: stloc.3
L_000f: ldloc.3
L_0010: ldc.i4.0
L_0011: stfld int32 MyTest.Program/c__DisplayClass2::i
L_0016: br.s L_0044
L_0018: nop
L_0019: ldloc.2
L_001a: brtrue.s L_002b
L_001c: ldloc.3
L_001d: ldftn instance void MyTest.Program/c__DisplayClass2::
.class auto ansi sealed nested private beforefieldinit c__DisplayClass2
extends [mscorlib]System.Object
{
.custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()
.method public hidebysig specialname rtspecialname instance void .ctor() cil managed
{
}
.method public hidebysig instance void
static void Main(string[] args)
{
List lists = new List();
TempClass tempClass = new TempClass();
for (tempClass.i = 0; tempClass.i 5; tempClass.i++)
{
Action t = tempClass.TempFuc;
lists.Add(t);
}
foreach (Action t in lists)
{
t();
}
}
class TempClass
{
public int i;
public void TempFuc()
{
Console.WriteLine(i.ToString());
}
}
static void Main(string[] args)
{
List lists = new List();
for (int i = 0; i 5; i++)
{
int temp = i;
Action t = () =>
{
Console.WriteLine(temp.ToString());
};
lists.Add(t);
}
foreach (Action t in lists)
{
t();
}
}
static void Main(string[] args)
{
List lists = new List();
for (int i = 0; i 5; i++)
{
TempClass tempClass = new TempClass();
tempClass.i = i;
Action t = tempClass.TempFuc;
lists.Add(t);
}
foreach (Action t in lists)
{
t();
}
}
class TempClass
{
public int i;
public void TempFuc()
{
Console.WriteLine(i.ToString());
}
}
文章标题:【转】编写高质量代码改善C#程序的157个建议——建议38:小心闭包中的陷阱
文章链接:http://soscw.com/index.php/essay/79006.html