这些例子是C#闭包吗?

13

我仍然不太理解闭包是什么,所以我发布了这两个示例,想知道这两个示例是否都是闭包?

示例 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个回答

9
您的第二个示例使用了闭包(严格来说,无论哪种情况,编译器都会计算闭包,但在第一种情况下您没有使用它)。
闭包只是“此函数可见的所有变量”。没有更多,也没有更少。显然,在这两种情况下,这些变量都存在,并由编译器确定。
但是,当我们谈论“使用闭包”时,通常指的是lambda表达式可以使用在其声明处可见的所有局部变量。它们都是其闭包的一部分。
在您的情况下,d只是lambda函数的参数,因为您在第一种情况下只使用了它,因此您并没有真正利用闭包。
在第二种情况下,filter不是在lambda表达式中定义的,它既不是参数也不是其他任何东西。它是一个本地变量,恰好在声明lambda的位置可见。因此,它是lambda的闭包的一部分,这使您可以在lambda的主体中引用它。
编辑
如评论中指出的那样,我没有仔细阅读您的代码。我只注意到了每个示例中的第二个lambda表达式。第一个lambda确实使用了闭包(它在两种情况下都封闭在subFolders上)。

2
第一个例子闭合了“subFolders”,因此它也是一个闭包。 - Konrad Rudolph
哦,对了,我甚至没有注意到第一个lambda。我只看到了d =>...这个。你是对的。第一个lambda闭合在subFolders上,而在第二种情况下,第二个lambda闭合在filter上。 - jalf

6
是的,闭包其实就是一个函数,它可以“保存”一些变量,这些变量来自于定义它的环境。所以在你的两个例子中,定义的action函数保存了名为subFolders的列表,即使局部变量超出了作用域,该函数仍然可以引用它。第二个例子中的filter变量也被定义的函数保存。
更精确的定义请参见这里

0
这是一个来自http://www.agileatwork.com/a-proper-closure-in-csharp/的例子,如果你熟悉JavaScript,可能会更加熟悉。在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; }

    ...
}

0

这两个示例都有闭包。在"A"中,匿名方法捕获了局部变量subFolders。在"B"中,匿名方法捕获了局部变量subFolders和filter。此外,您还可以在这里查看。

P.S. 还请注意,在"A"中实际上使用了闭包,因为您正在使用subFolders变量。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接