这是C#中闭包的一个例子吗?

Nib*_*Pig 8 c# closures

static void Main(string[] args)
{
    var s = 3;

    Func<int, Func<int>> func = 
        x => () =>
    {
        return x;
    };

    var result1 = func(s);

    func = null;

    s = 5;

    var result2 = result1();

    Console.WriteLine(result2);


    Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)

我的理解是x实际上并没有被声明为变量,例如.var x = 3.相反,它被传递给外部函数,后者返回一个返回原始值的函数.在它返回时,它会在x周围创建一个闭包来记住它的值.然后,如果你改变s,它没有任何效果.

这是正确的吗?

(顺便说一下输出是3,我期待).

编辑:这是我认为的原因图

关闭示例

将x = 3传递给func,它返回一个只返回x的函数.但是在内部函数中不存在x,只有它的父元素,并且在我使其为空后它的父元素不再存在.当内部函数运行时,x存储在哪里?它必须从父级创建一个闭包.

进一步澄清:

    int s = 0;

    Func<int, Func<int>> func = 
        x => () =>
    {
        return x;
    };

    for (s = 0; s < 5; s++)
    {
        var result1 = func(s);

        var result2 = result1();

        Console.WriteLine(result2);
    };
Run Code Online (Sandbox Code Playgroud)

输出为0,1,2,3,4

但是用你的例子:

static void Main(string[] args)
{
    int s = 0;

    Func<int, Func<int>> func = 
        x => () =>
    {
        return s;
    };

    List<Func<int>> results = new List<Func<int>>();

    for (s = 0; s < 5; s++)
    {
         results.Add(func(s));
    };

    foreach (var b in results)
    {
        Console.WriteLine(b());
    }

    Console.ReadKey();
}
Run Code Online (Sandbox Code Playgroud)

输出是5 5 5 5 5,这不是你想要的.它没有捕获变量的值,它只是保留了对原始s的引用.

闭包是在javascript中精确创建的,以避免此问题.

Jam*_*are 12

不,这不是一个闭包s,x只是lambda表达式的一个参数,它生成一个委托(实际上可以简化为x => () => x.

        Func<int, Func<int>> func = x => () => x;

        var a = func(3);

        var b = func(5);

        Console.WriteLine(a());
        Console.WriteLine(b());
Run Code Online (Sandbox Code Playgroud)

你会得到35预期的一样.在两种情况下它实际上记得x从另一个时间,但它确实收于x外的λ的持续期间本地.

基本上,每次调用x => () => x都会创建一个新的() => x委托,它捕获本地值x(不是s传入的).

即使您使用s,并传递它,并改变它,你仍然可以35:

        int s = 3;
        Func<int, Func<int>> func = x => () => x;

        var a = func(s);

        s = 5;

        var b = func(s);

        // 3 and 5 as expected
        Console.WriteLine(a());
        Console.WriteLine(b());
Run Code Online (Sandbox Code Playgroud)

现在,它确实捕捉局部变量x 的局部功能,这是在生成的每一个电话.因此x,在较大的lambda的调用之间不会持续存在,但如果稍后在lambda中进行更改,它将被捕获.

例如,让我们说你有这个:

        int s = 3;
        Func<int, Func<int>> func = x => 
            () => {
                      Func<int> result = () => x;
                      x = 10 * x;
                      return result;
                   };

        var a = func(s);

        s = 5;

        var b = func(s);
Run Code Online (Sandbox Code Playgroud)

在这种情况下,x匿名方法内的闭包更为明显.运行它的结果将是30和50,因为x在匿名方法内的修改会影响x本地对该匿名方法的闭包,但是,这些不会在调用之间进行,因为它只捕获x传递给匿名方法的本地方法,而不是s习惯称之为.

所以,总结一下,你的图,例如:*主要传递s到更大的拉姆达(FUNC你的图中)*FUNCx时,它被称为生成匿名方法的时候() => x

一旦调用外部lambda(func),闭合器就开始和结束,因为它只对那个lambda是局部的,并且不会从main接近任何东西.

这有帮助吗?

  • @SLC:杰西在另一条评论中指出,关键点似乎是绊倒你.闭包"捕获_variable_,而不是_value_". (2认同)
  • @SLC:有时候它肯定会很疯狂,幸运的是有代码辅助工具(ReSharper等)会警告你,如果你正在修改一个闭包,那么你可以检查这是否真的是你想要做的. (2认同)