Замыкания часто связаны с функциональными языками программирования. Замыкания подключают функцию к ее ссылающейся среде, позволяя функции получать доступ к нелокальным переменным. В C # замыкания поддерживаются с помощью анонимных методов, лямбда-выражений и делегатов.

Я обсуждал анонимные методы а также лямбда-выражения в предыдущих статьях. Так что же делегат? Делегат – это типобезопасный указатель на функцию, который может ссылаться на метод, имеющий ту же сигнатуру, что и у делегата. Делегаты используются для определения методов обратного вызова и реализации обработки событий.

В этой статье рассказывается о том, как мы можем работать с замыканиями, используя анонимные методы, лямбда-выражения и делегаты в C #. Для работы с примерами кода, приведенными в этой статье, в вашей системе должна быть установлена ​​Visual Studio 2019. Если у вас еще нет копии, вы можете скачать Visual Studio 2019 здесь.

Создайте проект консольного приложения в Visual Studio

Прежде всего, давайте создадим проект консольного приложения .NET Core в Visual Studio. Предполагая, что в вашей системе установлена ​​Visual Studio 2019, выполните действия, описанные ниже, чтобы создать новый проект консольного приложения .NET Core.

  1. Запустите интегрированную среду разработки Visual Studio.
  2. Нажмите «Создать новый проект».
  3. В окне «Создать новый проект» выберите «Консольное приложение (.NET Core)» из отображаемого списка шаблонов.
  4. Нажмите “Далее.
  5. В окне «Настроить новый проект» укажите имя и расположение для нового проекта.
  6. Щелкните “Создать”.

Мы будем использовать этот проект, чтобы проиллюстрировать использование анонимных методов, лямбда-выражений и делегатов в качестве замыканий в следующих разделах этой статьи.

Замыкание как первоклассная функция в C #

Замыкание определяется как функция первого класса, содержащая свободные переменные, связанные в лексической среде. В языке программирования C # функция первого класса рассматривается как первоклассный тип данных. Это означает, что вы можете назначить функцию переменной, вызвать ее или передать ее так же, как вы работаете с любым другим первоклассным типом данных.

Замыкание – это особый тип функции, которая внутренне связана со средой, в которой на нее ссылаются. В результате замыкания могут использовать переменные, относящиеся к ссылающейся среде, несмотря на то, что эти значения выходят за рамки замыкания.

Примеры простых замыканий в C #

Вы можете написать закрытие, используя анонимный метод, как показано в приведенном ниже фрагменте кода.

Func<string, string> someFunc = delegate (string someVariable)
{
   return "Hello World!";
};

В качестве альтернативы вы можете создать закрытие с помощью лямбда-функции, как показано в фрагменте кода ниже.

Func<string, string> someFunc = someVariable => "Hello World!";

Обратите внимание, что оба приведенных выше фрагмента кода создают метод, который принимает строку в качестве параметра и возвращает другую строку. Вот как вы можете вызвать любое из этих замыканий, которые мы только что создали:

string str = someFunc("This is a demo");

Давайте посмотрим на другой пример. Приведенный ниже фрагмент кода создает целочисленное значение в нелокальной переменной с именем x.

int x = 10;
Action closure = delegate
{
     Console.WriteLine("The value of the non-local variable x is: {0}", x);
};
closure();

Вот как вы можете сделать то же самое, используя лямбда-выражение:

int x = 10;
Action closure = () =>
{
    Console.WriteLine("The value of the non-local variable x is: {0}", x);
};
closure();

В обоих случаях результат будет выглядеть точно так, как показано на рисунке 1 ниже.

закрытие csharp 01 IDG

Рисунок 1: Наше простое закрытие в действии!

Замыкания захватывают переменные, а не значения

Поскольку замыкание привязано к среде, в которой оно объявлено, оно может ссылаться на переменные и объекты вне области видимости из своего тела. Вот пример, иллюстрирующий это:

int x = 10;
Action a = delegate { Console.WriteLine($"The value of x is: {x}"); };
a();

Когда вы выполните приведенный выше код, результат появится в окне консоли, как показано на рисунке 2.

закрывает csharp 02 IDG

Рисунок 2: Замыкание может ссылаться на нелокальные переменные и объекты в своем теле.

В следующем фрагменте кода показано, что анонимный метод привязан к переменным в теле родительского метода, а не к значениям.

int x = 10;
Action a = delegate { Console.WriteLine($"The value of x is: {x}"); };
x = 100;
a();

Когда вы выполните приведенный выше код, выходные данные будут выглядеть, как показано на рисунке 3. Обратите внимание, что анонимная функция возвращает 10, а не 100.

закрытие csharp 03 IDG

Рисунок 3: Замыкание привязано к переменным, а не значениям тела родительского метода.

Как работают замыкания в C #?

Когда компилятор C # обнаруживает делегат, который формирует закрытие, которое перемещается за пределы текущей области, делегат и связанные с ним локальные переменные продвигаются в класс, созданный компилятором. Оттуда требуется небольшая магия компилятора, чтобы перемещаться между экземплярами класса, созданного компилятором, гарантируя, что каждый раз при вызове делегата вызывается функция этого класса. Когда больше нет ссылок на экземпляр этого класса, сборщик мусора собирает этот экземпляр так же, как и другие экземпляры.

Ниже приведен пример класса, созданного компилятором, созданного при компиляции фрагмента кода, содержащего замыкания.

[CompilerGenerated]
 private sealed class <>c__DisplayClass0_0
    {
        public int x;
        internal void <M>b__0()
        {
            Console.WriteLine(string.Format("The value of x is: {0}", x));
        }
    }

Чтобы лямбда оставалась «вызываемой», переменные, на которые она ссылается, должны сохраняться даже после того, как функция, в которой они были определены, завершила выполнение. Для этого C # использует классы. Итак, когда лямбда-функция обращается к переменной, которая определена внутри функции, эта переменная извлекается и помещается в новый класс, созданный компилятором. Именно так работает закрытие!

Замыкания возникли в мире функционального программирования, но они нашли место и в объектно-ориентированных языках программирования. Замыкания по своей сути не обеспечивают возможности компоновки – все, что они делают, это упрощают реализацию делегатов. Я расскажу больше о закрытии в будущих публикациях здесь.

Авторские права © 2021 IDG Communications, Inc.


#Как #использовать #замыкания

Source link