Что такого особенного в закрытии?
Я был читал эту статью о закрытии, в которой они говорят:
- "вся сантехника автоматическая"
- компилятор "создает класс оболочки" и "продлевает срок службы переменных"
- "вы можете использовать локальные переменные без проблем"
- компилятор .NET заботится о сантехнике для вас и т.д.
Итак, я сделал пример, основанный на их коде и мне, кажется, что закрытие просто действует аналогично обычным именованным методам, которые также "заботятся о локальных переменных без беспокойства" и в которых "вся автоматическая сантехника",
Или какая проблема сделала эту "упаковку локальных переменных", что делает закрытие настолько особенным/интересным/полезным?
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;
}
}
}
Ответ
Итак, ответ на этот вопрос - читать статью статью Джона Скита о закрытии, о которой указал Марк. Эта статья не только показывает эволюцию, ведущую к лямбда-выражениям на С#, но также показывает, как закрытие рассматривается в Java, отличное чтение для этой темы.
Ответы
Ответ 1
Ваш пример не ясен и не показывает (IMO) типичное использование захвата (единственное, что было зафиксировано a
, которое всегда 3, поэтому не очень интересно).
Рассмотрим этот пример учебника (предикат):
List<Person> 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;
}
}
Метод захвата дополнительно распространяется на множество переменных в разных областях - скрытая часть сложности.
Помимо названий, приведенное выше является приближением того, что компилятор С# делает за кулисами. Обратите внимание, что когда задействуются дополнительные области действия, мы начинаем цепочку различных классов захвата (т.е. Внутренние области имеют ссылку на класс захвата внешних областей). Весьма сложный.
У Jon Skeet есть хорошая статья об этом здесь и более в его книге.
Ответ 2
Закрытие - это функциональность компилятора. Вы не видите этого, это просто делает код, который вы пишете.
Без него вызов AddToIt (3) завершится неудачно, поскольку базовая lamda использует локальную переменную a = 27 в области AddToItClusure(). Эта переменная не существует при вызове AddToIt.
Но из-за механизма Closure, используемого компилятором, код работает, и вам не нужно заботиться об этом.