什么是.NET中的'闭包'?

Dev*_*per 190 .net closures

什么是关闭?我们在.NET中有它们吗?


如果它们确实存在于.NET中,您能否提供一个代码片段(最好用C#)来解释它?


编辑:我浏览了Jon Skeet的文章,了解什么是闭包以及如何在.NET中使用它们.

Jon*_*eet 247

我有一篇关于这个主题的文章.(它有很多例子.)

从本质上讲,闭包是一个代码块,可以在以后执行,但它维护它首次创建的环境 - 即它仍然可以使用创建它的方法的局部变量等,即使在那之后方法已完成执行.

闭包的一般特性是通过匿名方法和lambda表达式在C#中实现的.

这是一个使用匿名方法的示例:

using System;

class Test
{
    static void Main()
    {
        Action action = CreateAction();
        action();
        action();
    }

    static Action CreateAction()
    {
        int counter = 0;
        return delegate
        {
            // Yes, it could be done in one statement; 
            // but it is clearer like this.
            counter++;
            Console.WriteLine("counter={0}", counter);
        };
    }
}
Run Code Online (Sandbox Code Playgroud)

输出:

counter=1
counter=2
Run Code Online (Sandbox Code Playgroud)

在这里我们可以看到CreateAction返回的操作仍然可以访问计数器变量,并且确实可以增加它,即使CreateAction本身已经完成.

  • 谢谢乔恩.顺便说一句,你有没有在.NET中不知道的东西?:)当你有问题时,你会去哪儿? (56认同)
  • 总是有更多要学习:)我刚刚通过C#读完CLR - 非常有用.除此之外,我经常向Marc Gravell询问WCF /绑定/表达式树,以及Eric Lippert用于C#语言的事情. (44认同)
  • 我会说闭包没有用,除非它们可以被执行,并且"稍后"突出了能够捕获环境的"奇怪"(否则可能会因执行时间而消失).如果你只引用*一半的句子,那么它当然是一个不完整的答案. (11认同)
  • @SLC:是的,`counter`可以递增 - 编译器生成一个包含`counter`字段的类,任何引用`counter`的代码最终都会通过该类的实例. (3认同)
  • 我注意到了,但我仍然认为你关于它是"可以在以后执行的代码块"的陈述是完全错误的 - 它与执行无关,更多地与变量值和范围有关而不是执行,本身. (2认同)
  • 要添加的东西,闭包存储为引用,即使它是值类型.如果你玩回归函数的函数,你会看到这个:) (2认同)
  • @SLC:在我给出的示例中,它是源代码中的局部变量 - 但正如我所说,编译器创建一个*的实例,其中*包含*一个字段.`counter`本身仍然存储在一个值类型中,但它存储在*类的一个实例中.我建议你编译上面的代码,然后在Reflector中查看它,没有优化:) (2认同)

小智 22

如果您有兴趣了解C#如何实现Closure,请阅读"我知道答案(其42)博客"

编译器在后台生成一个类来封装anoymous方法和变量j

[CompilerGenerated]
private sealed class <>c__DisplayClass2
{
    public <>c__DisplayClass2();
    public void <fillFunc>b__0()
    {
       Console.Write("{0} ", this.j);
    }
    public int j;
}
Run Code Online (Sandbox Code Playgroud)

对于功能:

static void fillFunc(int count) {
    for (int i = 0; i < count; i++)
    {
        int j = i;
        funcArr[i] = delegate()
                     {
                         Console.Write("{0} ", j);
                     };
    } 
}
Run Code Online (Sandbox Code Playgroud)

把它变成:

private static void fillFunc(int count)
{
    for (int i = 0; i < count; i++)
    {
        Program.<>c__DisplayClass1 class1 = new Program.<>c__DisplayClass1();
        class1.j = i;
        Program.funcArr[i] = new Func(class1.<fillFunc>b__0);
    }
}
Run Code Online (Sandbox Code Playgroud)


Dan*_*ego 9

闭包是保留原始范围的变量值的功能值.C#可以以匿名委托的形式使用它们.

举个简单的例子,拿这个C#代码:

    delegate int testDel();

    static void Main(string[] args)
    {
        int foo = 4;
        testDel myClosure = delegate()
        {
            return foo;
        };
        int bar = myClosure();

    }
Run Code Online (Sandbox Code Playgroud)

在它结束时,bar将被设置为4,并且myClosure委托可以被传递以在程序的其他地方使用.

闭包可用于许多有用的事情,如延迟执行或简化接口 - LINQ主要使用闭包构建.它对大多数开发人员来说最直接的方式是为动态创建的控件添加事件处理程序 - 您可以使用闭包在实例化控件时添加行为,而不是将数据存储在其他位置.


Ant*_*nes 7

Func<int, int> GetMultiplier(int a)
{
     return delegate(int b) { return a * b; } ;
}
//...
var fn2 = GetMultiplier(2);
var fn3 = GetMultiplier(3);
Console.WriteLine(fn2(2));  //outputs 4
Console.WriteLine(fn2(3));  //outputs 6
Console.WriteLine(fn3(2));  //outputs 6
Console.WriteLine(fn3(3));  //outputs 9
Run Code Online (Sandbox Code Playgroud)

闭包是在创建它的函数之外传递的匿名函数.它维护它所使用的函数所在的任何变量.