这些例子是C#闭包吗?

时间:2009-10-23 08:31:09

标签: c# closures

我仍然不太明白闭包是什么,所以我发布了这两个例子,我想知道这些例子是否都是闭包?

示例A:

List<DirectoryInfo> subFolders = new List<DirectoryInfo>();

Action<string> FilterSubFoldersStartA =
  s => subFolders.
       AddRange((new DirectoryInfo(s)).GetDirectories().
       Where(d => d.Name.StartsWith("A")));

FilterSubFoldersStartA(@"c:\tempa");
FilterSubFoldersStartA(@"c:\tempb");

例B:

List<DirectoryInfo> subFolders = new List<DirectoryInfo>();

string filter = "A";

Action<string> FilterSubFoldersStartGen =
  s => subFolders.
       AddRange((new DirectoryInfo(s)).GetDirectories().
       Where(d => d.Name.StartsWith(filter)));

FilterSubFoldersStartGen(@"c:\tempa");

filter = "B"; 

FilterSubFoldersStartGen(@"c:\tempb");

4 个答案:

答案 0 :(得分:9)

你的第二个例子使用了闭包(从技术上讲,你可以说编译器在两种情况下都会计算闭包,但在第一种情况下你没有使用它。)

闭包只是“此函数可见的所有变量”。没有更多,没有更少。显然,在这两种情况下,这些变量都存在,并由编译器确定。

但是当我们谈论“使用闭包”时我们通常的意思是lambda表达式可以使用它们声明的位置处可见的所有局部变量。它们都是关闭的一部分。

在你的情况下,d只是lambda函数的参数,因为你在第一种情况下使用了所有这些,所以你并没有真正利用闭包。

在第二种情况下,filter未在lambda表达式中定义,它不是参数或任何东西。它是一个局部变量,恰好在声明lambda的地方可见。因此它是lambda闭包的一部分,它允许你在lambda的主体中引用它。

修改
正如评论中指出的那样,我没有仔细阅读您的代码。我只注意到每个例子中的第二个lambda表达式。第一个lambda确实使用闭包(在两种情况下它都会关闭subFolders。)

答案 1 :(得分:6)

是的,闭包只不过是一个从定义它的环境中“保存”一些变量的函数。因此,在两个示例中,定义的操作都会保存名为subFolders的列表,即使在局部变量超出范围之后,这些列表也可以由函数引用。第二个示例中的filter变量也由定义的函数保存。

更精确的定义here

答案 2 :(得分:0)

这两个例子都有闭包。在“A”中,匿名方法捕获局部变量子文件。在“B”中,匿名方法捕获局部变量subFolders和filter。另请查看here

P.S。另请注意,您实际上是在“A”中使用闭包,因为您正在使用subFolders变量。

答案 3 :(得分:0)

如果您对JavaScript感到满意,可以从http://www.agileatwork.com/a-proper-closure-in-csharp/看一些可能看起来更熟悉的示例。在tax变量周围创建一个闭包,因此计算只进行一次。它在眼睛上并不容易,但你可以在C#中做这类事情很酷。

public class Order
{
    public Order(ITaxCalculator taxCalculator)
    {
        CalculateTax = new Func<Func<decimal>>(() =>
        {
            decimal? tax = null;
            return () =>
            {
                if (!tax.HasValue)
                {
                    tax = taxCalculator.Calculate(this);
                }
                return tax.Value;
            };
        })();
    }

    public Func<decimal> CalculateTax { get; set; }

    ...
}