.Net 闭包理解

.Net 闭包理解#

这个东西初看是比较难懂,但是一旦理解之后就很容易了,做笔记以加深印象。且看这题

example.1

class Program
{
    static void Main(string[] args)
    {
        var actions = new List<Action>();

        for (int i = 0; i < 5; i++)
        {
            actions.Add(() =>
            {
                Print(i);
            });
        }

        actions.ForEach(x => x());
    }

    private static void Print(int i)
    {
        Console.WriteLine(i);
    }
}

运行一下,结果都是5,Why?首先我们都知道,程序执行一个函数的时候,会将这个函数中用到的局部
变量压入一个栈中,退出这个函数的时候,会将栈中的数据清空。上面的代码,第一个Print函数用的i
值应该是第一个i(也就是0)所在地址的值,但是自加之后,地址变了,那么第一个Print函数用的i(也就是0)去
哪里了?我理解的话应该就是变成了一个不安全的地址,访问这个值就会出现问题。为了避免这种情况
的发生,.Net是如何处理的呢,那就是在所有Print函数执行的时候,不让这个i被清理,于是提升了i的
作用域(这个现象就叫闭包),我换个写法

example.2

class Program
{
    private static int _i;

    static void Main(string[] args)
    {
        var actions = new List<Action>();

        for (_i = 0; _i < 5; _i++)
        {
            actions.Add(() =>
            {
                Print(_i);
            });
        }

        actions.ForEach(x => x());
    }

    private static void Print(int i)
    {
        Console.WriteLine(i);
    }
}

_注意:_此时是不存在闭包现象的,当然也别以为.Net就是这么做的,并不是。通过IL反汇编工具查看
example.1的代码,发现它把i封装到了一个匿名的类中,可以这么想,要使一个东西在栈上不被清理,
那就把它放到堆上...,于是用了个匿名类把i当成其属性,代码如下:

class Program
{
    static void Main(string[] args)
    {
        var actions = new List<Action>();

        var myClass = new MyClass();

        for (var i = 0; i < 5; i++)
        {
            myClass.i = i;

            actions.Add(() =>
            {
                Print(myClass);
            });
        }

        myClass.i++;

        actions.ForEach(x => x());
    }

    private static void Print(MyClass myClass)
    {
        Console.WriteLine(myClass.i);
    }
}

internal class MyClass
{
    public int i
    {
        get; set;
    }
}

如果我们要避免这种情况,该如何做?那就相当于上诉代码每一个循环使用一个新的MyClass

class Program
{
    static void Main(string[] args)
    {
        var actions = new List<Action>();

        for (var i = 0; i < 5; i++)
        {
            var myClass = new MyClass();

            myClass.i = i;

            actions.Add(() =>
            {
                Print(myClass);
            });
        }

        actions.ForEach(x => x());
    }

    private static void Print(MyClass myClass)
    {
        Console.WriteLine(myClass.i);
    }
}

internal class MyClass
{
    public int i
    {
        get; set;
    }
}

其实申明MyClass这一步也可以省了,就是如下

class Program
{
    static void Main(string[] args)
    {
        var actions = new List<Action>();

        for (var i = 0; i < 5; i++)
        {
            var i1 = i;
            actions.Add(() =>
            {
                Print(i1);
            });
        }

        actions.ForEach(x => x());
    }

    private static void Print(int i)
    {
        Console.WriteLine(i);
    }
}

其实上面这个写法是Resharper提示闭包之后给出的解决方案。
以上,就是我看了别人的一些理解,再加上自己的一些理解推敲出来的东西,有许多地方是模糊的,表面的,但
闭包这东西,不理解好像也没什么影响,这里起个抛砖引玉的作用,欢迎斧正拍砖。

posted @ 2016-03-28 22:24  UncleNull  阅读(857)  评论(0编辑  收藏  举报