I've been reading this article about closures in which they say:
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;
}
}
}
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.
Closures are important because they control what is and isn't in scope in a particular function, along with which variables are shared between sibling functions in the same containing scope.
The part of the environment which gives the free variables in an expression their meanings, is the closure. It is called this way, because it turns an open expression into a closed one, by supplying these missing definitions for all of its free variables, so that we could evaluate it.
Definition of closure 1 : an act of closing : the condition of being closed closure of the eyelids business closures the closure of the factory. 2 : an often comforting or satisfying sense of finality victims needing closure also : something (such as a satisfying ending) that provides such a sense.
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).
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.
Jon Skeet has a good article on this here, and more in his book.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With