美文网首页
C#闭包陷阱

C#闭包陷阱

作者: 祝你万事顺利 | 来源:发表于2019-05-31 14:46 被阅读0次

    在C#中,lambda(匿名委托)使用时,编译器会自动生成一个类来保存lambda中的方法以及字段,当lambda引用外部变量的时候,编译器会在生成的类中创建一个字段保存引用到的变量。
    在for循环定义的循环变量是for块级别的,没有在循环迭代时候创建新的变量。也就是说想在for循环中的lambda执行的时候调用外部变量将只能获取最新的值,所以我们需要在for循环中创建一个临时变量这样lambda将这个临时变量的值存起来。

    用例:

    class Program
        {
            static void Main(string[] args)
            {
                List<Person> li = new List<Person>()
                {
                    new Person("kk"),
                    new Person("cc"),
                };
                Action[] array = new Action[2];
                for (int i = 0; i < 2; i++)
                {
                    array[i] = () => { Console.WriteLine(li[i].name);};
                }
                for (int i = 0; i < 2; i++)
                {
                    array[i]();
                }
            }
        }
        public class Person
        {
            public string name;
    
            public Person()
            {
            }
    
            public Person(string name)
            {
                this.name = name;
            }
        }
    

    反编译
    反编译可以看到在Main中有一个<>c__DisplayClass0_2.i,此变量会在复制后变为赋值时循环的次数加一

    private static void Main(string[] args)
            {
                Program.<>c__DisplayClass0_0 <>c__DisplayClass0_ = new Program.<>c__DisplayClass0_0();
                <>c__DisplayClass0_.li = new List<Person>
                {
                    new Person("kk"),
                    new Person("cc")
                };
                Action[] array = new Action[2];
                Program.<>c__DisplayClass0_1 <>c__DisplayClass0_2 = new Program.<>c__DisplayClass0_1();
                <>c__DisplayClass0_2.CS$<>8__locals1 = <>c__DisplayClass0_;
                <>c__DisplayClass0_2.i = 0;
                while (<>c__DisplayClass0_2.i < 2)
                {
                    array[<>c__DisplayClass0_2.i] = new Action(<>c__DisplayClass0_2.<Main>b__0);
                    int i = <>c__DisplayClass0_2.i;
                    <>c__DisplayClass0_2.i = i + 1;
                }
                for (int j = 0; j < 2; j++)
                {
                    array[j]();
                }
            }
    

    修改后:

    static void Main(string[] args)
            {
                List<Person> li = new List<Person>()
                {
                    new Person("kk"),
                    new Person("cc"),
                };
                Action[] array = new Action[2];
                for (int i = 0; i < 2; i++)
                {
                    int index = i;
                    array[index] = () => { Console.WriteLine(li[index].name);};
                }
                for (int i = 0; i < 2; i++)
                {
                    array[i]();
                }
            }
    

    反编译:

    private static void Main(string[] args)
            {
                Program.<>c__DisplayClass0_0 <>c__DisplayClass0_ = new Program.<>c__DisplayClass0_0();
                <>c__DisplayClass0_.li = new List<Person>
                {
                    new Person("kk"),
                    new Person("cc")
                };
                Action[] array = new Action[2];
                for (int i = 0; i < 2; i++)
                {
                    Program.<>c__DisplayClass0_1 <>c__DisplayClass0_2 = new Program.<>c__DisplayClass0_1();
                    <>c__DisplayClass0_2.CS$<>8__locals1 = <>c__DisplayClass0_;
                    <>c__DisplayClass0_2.index = i;
                    array[<>c__DisplayClass0_2.index] = new Action(<>c__DisplayClass0_2.<Main>b__0);
                }
                for (int j = 0; j < 2; j++)
                {
                    array[j]();
                }
            }
    

    相关文章

      网友评论

          本文标题:C#闭包陷阱

          本文链接:https://www.haomeiwen.com/subject/txwwtctx.html