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

时间:2012-06-12 14:39:24

标签: 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();
}

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

这是对的吗?

(顺便说一下输出为3,我期待)。

编辑:这是我认为为什么

的图表

Closure example

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);
    };

输出为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();
}

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

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

2 个答案:

答案 0 :(得分: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());

您将按预期获得35。在任何一种情况下它都不会实际记住x来自其他时间,但在外部lambda的持续时间内 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());

现在,它 捕获本地函数中的局部变量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);

在这种情况下,匿名方法中x上的闭包更为明显。运行此操作的结果将为30和50,因为匿名方法中对x的修改会影响该匿名方法本地x的闭包,但是,这些不会在调用之间延续,因为它和#39;仅捕获传入匿名方法的本地x,而不是用于调用它的s

总而言之,在您的图表和示例中: * main s传递给较大的lambda(图中的 func ) * func 在被调用以生成匿名方法时x关闭() => x

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

这有帮助吗?

答案 1 :(得分:4)

不,改变

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

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

关闭 s变量。