简体   繁体   English

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

[英]Is this an example of a closure in C#?

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

My understanding is that x is not actually declared as a variable eg. 我的理解是x实际上并没有被声明为变量,例如。 var x = 3. Instead, it's passed into the outer function, which returns a function that returns the original value. var x = 3.相反,它被传递给外部函数,后者返回一个返回原始值的函数。 At the time it is returning this, it creates a closure around x to remember its value. 在它返回时,它会在x周围创建一个闭包来记住它的值。 Then later on, if you alter s, it has no effect. 然后,如果你改变s,它没有任何效果。

Is this right? 这是正确的吗?

(Output is 3 by the way, which I'd expect). (顺便说一下输出是3,我期待)。

Edit: here's a diagram as to why I think it is 编辑:这是我认为的原因图

关闭示例

x=3 is passed into the func, and it returns a function that simply returns x. 将x = 3传递给func,它返回一个只返回x的函数。 But x doesn't exist in the inner function, only its parent, and its parent no longer exists after I make it null. 但是在内部函数中不存在x,只有它的父元素,并且在我使其为空后它的父元素不再存在。 Where is x stored, when the inner function is ran? 当内部函数运行时,x存储在哪里? It must create a closure from the parent. 它必须从父级创建一个闭包。

Further clarification: 进一步澄清:

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

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

However with your example: 但是用你的例子:

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

The output is 5 5 5 5 5, which isn't what you want. 输出是5 5 5 5 5,这不是你想要的。 It hasn't captured the value of the variable, it's merely retained a reference to the original s. 它没有捕获变量的值,它只是保留了对原始s的引用。

Closures are created in javascript precisely to avoid this problem. 闭包是在javascript中精确创建的,以避免此问题。

No, that's not a closure on s , x is just an argument to the lambda expression which generates a delegate (which could actually be simplified to x => () => x . 不,这不是ss闭包, 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());

You'll get 3 and 5 as expected. 你会得到35的预期。 In neither case does it actually remember x from the other time, but it does close on x locally during the duration of the outer lambda. 在两种情况下它实际上记得x从其他的时间,但它确实在接近x本地外拉姆达的持续期间。

Basically, each call to x => () => x will create a new () => x delegate which captures the local value of x (not of the s passed in). 基本上,每个呼叫到x => () => x将创建一个新的() => x代表其捕获的本地值x (未的s传递)。

Even if you use s and pass it in, and change it, you still get 3 and 5 : 即使你使用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());

Now, it does capture on the local variable x within the local function, which is generated at every call. 现在,它确实捕获本地函数中的局部变量x 该函数在每次调用时生成。 So the x won't persist between invocations of the larger lambda, but it will be captured if changed later in the lambda. 所以x不会在较大的lambda的调用之间持续存在,但如果稍后在lambda中进行更改,它将被捕获。

For example, let's say instead you had this: 例如,让我们说你有这个:

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

In this case, the closure on x within the anonymous method is more obvious. 在这种情况下,匿名方法中x的闭包更为明显。 The results of running this will be 30 and 50 because modifications to x within the anonymous method affect the closure on the x local to that anonymous method, however, these do not carry over between calls, because it's only capturing the local x passed into the anonymous method, and not the s used to call it. 运行它的结果将是30和50,因为在匿名方法中对x修改会影响该匿名方法的x local上的闭包,但是,这些不会在调用之间进行,因为它只捕获传递给它的本地x匿名方法,而不是用于调用它的s

So, to sum up, in your diagram and example: * main passes s into the larger lambda ( func in your diagram) * func closes on x at the time it's called to generate the anonymous method () => x 所以,综上所述,你的图和实施例中:· 通行证s到更大的拉姆达(您的图中FUNC)* FUNC上关闭x在它被称为产生匿名方法的时间() => x

Once the call to the outer lambda is made (func) the closer begins and ends because it's local to that lambda only and doesn't close on anything from main. 一旦调用外部lambda(func),闭合器就开始和结束,因为它只对那个lambda是局部的,并且不会从main接近任何东西。

Does that help? 这有帮助吗?

No, change 没变

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

to

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

and it closes over the s variable. 关闭s变量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM