Подтвердить что ты не робот

Что такого особенного в закрытии?

Я был читал эту статью о закрытии, в которой они говорят:

  • "вся сантехника автоматическая"
  • компилятор "создает класс оболочки" и "продлевает срок службы переменных"
  • "вы можете использовать локальные переменные без проблем"
  • компилятор .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, отличное чтение для этой темы.

4b9b3361

Ответ 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, используемого компилятором, код работает, и вам не нужно заботиться об этом.