157 recommendations for writing high-quality code to improve C # programs--Recommendation 38: Beware of traps in closures

Source: Internet
Author: User
Tags mscorlib

Recommendation 38: Beware of traps in closures

Let's take a look at the code below and imagine what the output is.

 static  void  Main (string  [] args) {List  <Action> lists = new  list<action> ();  for  (int  i = 0 ; I < 5 ;                I++ = () => foreach  (Action t in   lists) {T (); }        }

Our design intent is to have the anonymous method (shown here as a lambda expression) accept parameter I and output:

0

1

2

3

4

and the actual output is:

5

5

5

5

5

This code is not as simple as we thought, to fully understand how run-time code works, first we must understand what the C # compiler does for us.

The IL code is as follows:

. Method Private HidebysigStaticvoidMain (string[] args)CIL managed{    . EntryPoint    . Maxstack 3    . Locals Init (        [0]class[mscorlib] System.Collections.Generic.List '1<class[mscorlib]system.action> lists, [1]class[Mscorlib]system.action t, [2]class[Mscorlib]system.action cs$<>9__cachedanonymousmethoddelegate1, [3]classmytest.program/<>c__displayclass2 CS$<>8__LOCALS3, [4]BOOLcs$4$0000,        [5] ValueType [Mscorlib]system.collections.generic.list '1/enumerator<class[mscorlib] System.action> cs$5$0001)    l_0000: NOP     l_0001: newobj instance void[mscorlib] System.Collections.Generic.List '1<class[Mscorlib]system.action>::.ctor ()l_0006: stloc.0     l_0007: ldnull     l_0008: Stloc.2     l_0009: newobj instance voidMytest.program/<>c__displayclass2::.ctor ()l_000e: stloc.3     l_000f: Ldloc.3     l_0010: ldc.i4.0     l_0011: stfld Int32mytest.program/<>c__displayclass2::il_0016: BR.Sl_0044l_0018: NOP     l_0019: Ldloc.2     l_001a: BRTRUE.Sl_002bl_001c: Ldloc.3     l_001d: ldftn instance voidmytest.program/<>c__displayclass2::<main>b__0 ()l_0023: newobj instance void[mscorlib] System.action::.ctor (Object,nativeint)l_0028: Stloc.2     l_0029: BR.Sl_002bl_002b: Ldloc.2     l_002c: stloc.1     l_002d: ldloc.0     l_002e: Ldloc.1     l_002f: callvirt instance void[mscorlib] System.Collections.Generic.List '1<class[mscorlib] System.action>::ADD(!0)    l_0034: NOP     l_0035: NOP     l_0036: Ldloc.3     l_0037: DUP     l_0038: ldfld Int32mytest.program/<>c__displayclass2::il_003d: ldc.i4.1     l_003e: Add     l_003f: stfld Int32mytest.program/<>c__displayclass2::il_0044: Ldloc.3     l_0045: ldfld Int32mytest.program/<>c__displayclass2::il_004a: ldc.i4.5     l_004b: CLT     l_004d: Stloc.scs$4$0000    l_004f: Ldloc.scs$4$0000    l_0051: BRTRUE.Sl_0018l_0053: NOP     l_0054: ldloc.0 

The following ellipsis

On the l_0009 line , we found that the compiler created a class "<>c__displayclass2" For us, and each time within the loop, an instance variable I of the class is assigned a value.

The IL code for this class is:

. Class Auto ANSISealednested Private beforefieldinit<>c__displayclass2extends[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<main>b__0 ()CIL managed    {    }    . Field  Public Int32i}

After analysis, you will find that the preceding code is actually the same as the following code:

        Static voidMain (string[] args) {List<Action> lists =NewList<action>(); Tempclass Tempclass=NewTempclass ();  for(tempclass.i =0; Tempclass.i <5; tempclass.i++) {Action T=Tempclass.tempfuc; Lists.            ADD (t); }            foreach(Action tinchlists)            {T (); }        }        classTempclass { Public inti;  Public voidTempfuc () {Console.WriteLine (i.ToString ()); }        }

This code demonstrates the closure object. The so-called closure object refers to the Tempclass object in this case (in the first piece of code, the compiler generates the <>c__displayclass2 object for us). If an anonymous method (lambda expression) references a local variable, the compiler automatically promotes the reference to the closure object, modifying the variable i in the For loop to the public variable I that references the closure object. This way, even if the code execution leaves the scope of the original local variable I (such as a For loop), the scope that contains the closure object also exists. Understanding this, you understand the output of the code.

To implement the output expected at the beginning of this recommendation, you can place the generation of the closure object inside the For loop:

        Static voidMain (string[] args) {List<Action> lists =NewList<action>();  for(inti =0; I <5; i++)            {                inttemp =i; Action T= () + ={Console.WriteLine (temp.                ToString ());                }; Lists.            ADD (t); }            foreach(Action tinchlists)            {T (); }        }

This code is consistent with the following code:

        Static voidMain (string[] args) {List<Action> lists =NewList<action>();  for(inti =0; I <5; i++) {Tempclass Tempclass=NewTempclass (); Tempclass.i=i; Action T=Tempclass.tempfuc; Lists.            ADD (t); }            foreach(Action tinchlists)            {T (); }        }        classTempclass { Public inti;  Public voidTempfuc () {Console.WriteLine (i.ToString ()); }        }

Turn from: 157 recommendations for writing high-quality code to improve C # programs Minjia

157 recommendations for writing high-quality code to improve C # programs--Recommendation 38: Beware of traps in closures

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.