关闭有什么特别之处?
我一直在读这篇文章关于他们说的闭包 :
- “所有的管道都是自动的”
- 编译器“创建一个包装类”并“延长变量的生命周期”
- “你可以毫无顾虑地使用局部变量”
- .NET编译器为您处理管道等。
所以我根据他们的代码做了一个例子,对我而言,闭包似乎就像常规的命名方法一样,也“无需担心地处理局部变量”,其中“所有的管道都是自动的”。
或者这个“局部变量的包装”解决了什么问题,使得闭包如此特殊/有趣/有用?
using System; namespace TestingLambda2872 { class Program { static void Main(string[] args) { Func AddToIt = AddToItClosure(); Console.WriteLine("the result is {0}", AddToIt(3)); //returns 30 Console.ReadLine(); } public static Func AddToItClosure() { int a = 27; Func func = s => s + a; return func; } } }
回答
所以这个问题的答案是阅读Jon Skeet关于 Marc所指出的闭包的文章 。 本文不仅展示了在C#中导致lambda表达式的演变,还展示了如何在Java中处理闭包,这是本主题的优秀读物。
你的例子不清楚,并没有(IMO)显示典型的捕获用法(捕获的唯一东西是a
,它总是3,所以不是很有趣)。
考虑这个教科书示例(谓词):
List people = ... string nameToFind = ... Person found = people.Find(person => person.Name == nameToFind);
现在尝试没有关闭; 你需要做更多的工作,即使我们很懒惰:
PersonFinder finder = new PersonFinder(); finder.nameToFind = ... Person found = people.Find(finder.IsMatch); ... class PersonFinder { public string nameToFind; // a public field to mirror the C# capture public bool IsMatch(Person person) { return person.Name == nameToFind; } }
捕获方法进一步扩展到不同范围的许多变量 – 隐藏了很多复杂性。
除了名称之外,上面是C#编译器在幕后所做的近似。 请注意,当涉及其他范围时,我们开始链接不同的捕获类(即内部范围具有对外部范围的捕获类的引用)。 相当复杂。
Jon Skeet 在这里有一篇很好的文章 , 在他的书中有更多。
闭包是编译器的一个function。 你没有看到它,只是让你编写的代码工作。
没有它,对AddToIt(3)的调用将失败,因为底层的lamda在AddToItClusure()的范围内使用局部变量a = 27。 调用AddToIt时,此变量不存在。
但是由于Closure是编译器使用的一种机制,代码可以工作,你不必关心它。