[Заметки по изучению C#] Анонимные функции и лямбда-выражения

Вставьте сюда описание изображения


анонимная функция

Почему мы используем анонимные функции? Цель анонимных функций - упростить определение некоторых функций, особенно тех функций, которые будут вызываться только один раз после определения. Вместо того, чтобы перетаскивать файлы и определять их где-то в классе, анонимные функции будут более удобными и лаконичными.

Определение анонимной функции

Следующие две анонимные функции без параметров:

Action A = delegate {
    
     Debug.Log("Hello"); };
A += delegate () 
{
    
    
   Debug.Log("Hello"); 
};

Объявление анонимной функции необходимо использовать с делегатом, и его необходимо добавить в заголовок функции при ее объявлении delegate. В конструкции анонимной функции без параметров можно не использовать круглые скобки.
Определения анонимных функций не позволяют использовать дженерики (легко понять, что дженерики должны гибко принимать различные типы параметров при вызове функции, но использование анонимных функций означает, что она будет вызываться только один раз. Вместо использования дженериков, давайте прямо уточним)

Action<int, string> A = delegate (int i, string s)
{
    
    
    int q = Int32.Parse(s) + i;
};

Чтобы определить анонимную функцию с параметрами, просто определите ее как обычную функцию. Если вам нужно вернуть значение, просто используйте Func:

Func<int, string,int> A = delegate (int i, string s)
{
    
    
    int q = Int32.Parse(s) + i;
    return q;
};

Анонимная функция, переданная как параметр

В настоящее время существуют следующие определения:

    class Test
    {
    
    
        public Action action;
        public void Dosomething(int a ,Action fun)
        {
    
    
            fun();
        }
        public Action MyFun()
        {
    
    
            return delegate () {
    
     Debug.Log("返回委托类型的匿名函数"); };
        }
    }

В приведенном выше классе мы определили два метода: нам Dosomethingнужно передать Actionделегат fun, Myfunа затем вернуть Actionтип возвращаемого значения (определенный делегат может использоваться как тип возвращаемого значения), в котором мы используем анонимную функцию в качестве возвращаемого значения. значение типа делегата.

    void Start()
    {
    
    
        Test t = new Test();
        t.Dosomething(1, delegate {
    
     Debug.Log("匿名函数作为参数传入"); });
        Action A = delegate {
    
     Debug.Log("委托装载匿名函数并作为参数传入)"); };
        t.Dosomething(1, A);
    }

Анонимные функции можно передавать как параметры типов делегатов. Его также можно использовать в качестве типа делегата при возврате (предполагая, что анонимная функция должна автоматически инкапсулироваться как соответствующий делегат при передаче в качестве параметра):

    void Start()
    {
    
    
        Test t = new Test();
        t.action = t.MyFun();
        t.action(); // 输出:返回委托类型的匿名函数
        t.MyFun()(); // 只有委托返回值类型才能这么使用,调用函数后再加个括号直接调用委托
    }

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


Недостатки анонимных функций

Самым большим преимуществом использования анонимных функций является удобство, но самым большим недостатком анонимных функций является анонимность, как показано ниже:

Action A = delegate {
    
     Debug.Log("你好"); };
A -= delegate {
    
     Debug.Log("你好"); }; // 无效
A();// 依旧会输出你好,因为匿名函数无法删除
A = null; // 只有清空A才能从中删除匿名函数

Если мы хотим удалить анонимную функцию в делегате, даже если мы вычтем ту же самую определенную анонимную функцию из A, это не поможет, потому что эта функция не является той функцией. Анонимные функции не имеют имен функций и не могут быть вычтены.Даже если мы определим одну и ту же анонимную функцию, их адреса по сути не будут одной и той же функцией.

Таким образом, если делегат хранит только одну функцию, вы можете использовать анонимную функцию, но если делегат хранит несколько функций, тогда, когда вы хотите удалить анонимную функцию, вы можете очистить только делегат. Поэтому при проектировании использования анонимных функций либо используйте делегат для хранения только анонимной функции, либо передайте возвращаемое значение типа делегата в качестве параметра для вызова (как показано выше) t.MyFun()();.


лямбда-выражение

Что такое лямбда-выражение

Лямбда-выражение — это способ создания анонимных функций. Использование лямбда-выражений позволяет исключить delegateопределение анонимных функций:

Action A = () => {
    
     Debug.Log("你好"); };

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

        Action<int, string> B = (int i, string s) =>
        {
    
    
            int q = Int32.Parse(s) + i;
        };
        Func<int, string,int> C = (int i, string s) =>
        {
    
    
            int q = Int32.Parse(s) + i;
            return q;
        };
        // 委托已经指定类型,可以省略参数类型
        Func<int, string,int> C = (i, s) =>
        {
    
    
            int q = Int32.Parse(s) + i;
            return q;
        };

Когда лямбда-выражение выполняет только один оператор, даже фигурные скобки не нужны. Используйте пустые скобки, чтобы указать 0 входных параметров:

Action line = () => Console.WriteLine();

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

Action<int> square = x => Console.WriteLine("x");

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

Func<int, int> square = x => x * x; // return x*x,在带返回值类型的委托中省略return
Func<int, int, bool> testForEquality = (x, y) => x == y; //多个入参还是需要括号和逗号区分的

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

Func<int, int, int> constant = (_, _) => 42;

Закрытие

Конкретное описание замыканий было объяснено в этой статье [Заметки по изучению Lua] Lua Advanced — Функции и замыкания и не будет повторяться здесь.

Проще говоря, замыкание — это когда внутренняя функция ссылается на переменную внешней функции, в результате чего жизненный цикл внешней функции, которая должна быть освобождена в стеке, изменяется (расширяется). Эти переменные называются upvalues. Их исходная область действия — внешняя функция, но при использовании внутренней функции их область действия включает внутреннюю функцию.

Точно так же, как в следующем примере:

public event Action action;
public void Test()
{
    
    
    int value = 100;
    action = () => {
    
     Debug.Log(value); };//使用了value,但value作用域在函数Test而非匿名函数内
}

В приведенном выше примере мы использовали переменные внешней функции Test внутри анонимной функции value, и результат можно выполнить обычным образом. Само собой разумеется, что локальные переменные будут освобождены, valueесли Testони не используются, но использование анонимных функций продлевает их жизненный цикл.

Рассмотрим еще раз следующий пример:

public void Test()
{
    
    
    for (int i = 0; i < 10; i++)
    {
    
    
        action += () => {
    
     Debug.Log(i); };
    }
}
action();

Каков будет выходной ответ, когда мы выполним приведенный выше оператор? Вы можете подумать, что это 0123456789, но реальный ответ - вывести 10 раз по 10. Причина на самом деле очень проста. В делегате хранятся десять анонимных функций. Каждая инструкция предназначена для вывода, но не забывайте о верхнем значении, которое не iприсвоено iЛокальная переменная внутренней анонимной функции — это значение внешней функции, которую непосредственно вызывает анонимная функция i. Когда цикл заканчивается i=10, жизненный цикл, который должен быть освобожден, iпродлевается. Когда мы выполняем делегат, анонимная функция будет This iи this time i=10, следовательно, выводит 10 умножить на 10.

public void Test()
{
    
    
    for (int i = 0; i < 10; i++)
    {
    
    
    	int index = i;
        action += () => {
    
     Debug.Log(index); };
    }
}
action();

Однако int index = iвышеуказанную функцию можно реализовать с ее помощью, поскольку это переменная типа значения. Каждый раз, когда мы используем intее в цикле for index, мы фактически объявляем новую intпеременную. Таким образом, индекс для каждого вызова анонимной функции на самом деле представляет собой 10 различных индексов.

Если вы боитесь, что ваше лямбда-выражение случайно уловит увеличение значения внешней функции, вы можете staticограничить его с помощью ключевых слов:

Func<double, double> square = static x => x * x;

おすすめ

転載: blog.csdn.net/milu_ELK/article/details/132415187