关闭有什么特别之处?

时间:2022-06-20 20:27:38

I've been reading this article about closures in which they say:

我一直在读这篇文章关于他们说的闭包:

  • "all the plumbing is automatic"
  • “所有的管道都是自动的”

  • the compiler "creates a wrapper class" and "extends the life of the variables"
  • 编译器“创建一个包装类”并“延长变量的生命周期”

  • "you can use local variables without worry"
  • “你可以毫无顾虑地使用局部变量”

  • the .NET compiler takes care of the plumbing for you, etc.
  • .NET编译器为您处理管道等。

So I made an example based on their code and to me, it seems as though closures just act similarly to regular named methods which also "take care of the local variables without worry" and in which "all the plumbing is automatic".

所以我根据他们的代码做了一个例子,对我而言,闭包似乎就像常规的命名方法一样,也“无需担心地处理局部变量”,其中“所有的管道都是自动的”。

Or what problem did this "wrapping of local variables" solve that makes closures so special / interesting / useful?

或者这个“局部变量的包装”解决了什么问题,使得闭包如此特殊/有趣/有用?

using System;
namespace TestingLambda2872
{
    class Program
    {
        static void Main(string[] args)
        {
            Func<int, int> AddToIt = AddToItClosure();

            Console.WriteLine("the result is {0}", AddToIt(3)); //returns 30
            Console.ReadLine();
        }

        public static Func<int, int> AddToItClosure()
        {
            int a = 27;
            Func<int, int> func = s => s + a;
            return func;
        }
    }
}

Answer

So the answer to this one is to read Jon Skeet's article on closures that Marc pointed out. This article not only shows the evolution leading up to lambda expressions in C# but also shows how closures are dealt with in Java, an excellent read for this topic.

所以这个问题的答案是阅读Jon Skeet关于Marc所指出的闭包的文章。本文不仅展示了在C#中导致lambda表达式的演变,还展示了如何在Java中处理闭包,这是本主题的优秀读物。

2 个解决方案

#1


Your example isn't clear, and doesn't (IMO) show typical capture usage (the only thing captured is a, which is always 3, so not very interesting).

你的例子不清楚,并没有(IMO)显示典型的捕获用法(捕获的唯一东西是a,它总是3,所以不是很有趣)。

Consider this text-book example (a predicate):

考虑这个教科书示例(谓词):

List<Person> people = ...
string nameToFind = ...
Person found = people.Find(person => person.Name == nameToFind);

Now try it without a closure; you need to do a lot more work, even if we are lazy:

现在尝试没有关闭;你需要做更多的工作,即使我们很懒惰:

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

The capture approach extends further to lots of variables at different scopes - a lot of complexity that is hidden.

捕获方法进一步扩展到不同范围的许多变量 - 隐藏了很多复杂性。

Other than the names, the above is an approximation of what the C# compiler does behind the scenes. Note that when additional scopes are involved we start chaining the different capture classes (i.e. inner scopes have a reference to the capture class of outer scopes). Quite complex.

除了名称之外,上面是C#编译器在幕后所做的近似。请注意,当涉及其他范围时,我们开始链接不同的捕获类(即内部范围具有对外部范围的捕获类的引用)。相当复杂。

Jon Skeet has a good article on this here, and more in his book.

Jon Skeet在这里有一篇很好的文章,在他的书中有更多。

#2


The closure is a functionality of the Compiler. You don't see it, it just makes the code you write work.

闭包是编译器的一个功能。你没有看到它,只是让你编写的代码工作。

Without it, the call to AddToIt(3) will fail, because the underlying lamda uses the local variable a = 27 in the scope of AddToItClusure(). This variable is does not exist when AddToIt is called.

没有它,对AddToIt(3)的调用将失败,因为底层的lamda在AddToItClusure()的范围内使用局部变量a = 27。调用AddToIt时,此变量不存在。

But because of the Closure, a mechanism used by the compiler, the code works and you don't have to care about it.

但是由于Closure是编译器使用的一种机制,代码可以工作,你不必关心它。

#1


Your example isn't clear, and doesn't (IMO) show typical capture usage (the only thing captured is a, which is always 3, so not very interesting).

你的例子不清楚,并没有(IMO)显示典型的捕获用法(捕获的唯一东西是a,它总是3,所以不是很有趣)。

Consider this text-book example (a predicate):

考虑这个教科书示例(谓词):

List<Person> people = ...
string nameToFind = ...
Person found = people.Find(person => person.Name == nameToFind);

Now try it without a closure; you need to do a lot more work, even if we are lazy:

现在尝试没有关闭;你需要做更多的工作,即使我们很懒惰:

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

The capture approach extends further to lots of variables at different scopes - a lot of complexity that is hidden.

捕获方法进一步扩展到不同范围的许多变量 - 隐藏了很多复杂性。

Other than the names, the above is an approximation of what the C# compiler does behind the scenes. Note that when additional scopes are involved we start chaining the different capture classes (i.e. inner scopes have a reference to the capture class of outer scopes). Quite complex.

除了名称之外,上面是C#编译器在幕后所做的近似。请注意,当涉及其他范围时,我们开始链接不同的捕获类(即内部范围具有对外部范围的捕获类的引用)。相当复杂。

Jon Skeet has a good article on this here, and more in his book.

Jon Skeet在这里有一篇很好的文章,在他的书中有更多。

#2


The closure is a functionality of the Compiler. You don't see it, it just makes the code you write work.

闭包是编译器的一个功能。你没有看到它,只是让你编写的代码工作。

Without it, the call to AddToIt(3) will fail, because the underlying lamda uses the local variable a = 27 in the scope of AddToItClusure(). This variable is does not exist when AddToIt is called.

没有它,对AddToIt(3)的调用将失败,因为底层的lamda在AddToItClusure()的范围内使用局部变量a = 27。调用AddToIt时,此变量不存在。

But because of the Closure, a mechanism used by the compiler, the code works and you don't have to care about it.

但是由于Closure是编译器使用的一种机制,代码可以工作,你不必关心它。