[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전달한 다음 반환 값 유형을 반환해야 합니다(정의된 대리자를 반환 값 유형으로 사용할 수 있음). 여기서 익명 함수를 반환 값으로 사용합니다. 대리자 유형의 값입니다.ActionfunMyfunAction

    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 Study Notes] Lua Advanced - Functions and Closures 에 설명되어 있으며 여기서는 반복하지 않겠습니다.

간단히 말해서, 클로저는 내부 함수가 외부 함수의 변수를 참조하여 스택에서 해제되어야 하는 외부 함수의 수명 주기가 변경(확장)되는 경우입니다. 이 변수를 upvalue라고 하며 원래 범위는 외부 함수이지만 내부 함수를 사용하면 해당 범위에 내부 함수가 포함됩니다.

다음 예와 같습니다.

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을 출력하는 것입니다. 이유는 사실 매우 간단합니다. 델리게이트에는 10개의 익명 함수가 저장되어 있습니다. 각 명령어는 출력을 위한 것이지만 upvalue를 잊지 마세요. i할당 되지 않음 i내부 익명 함수의 지역 변수는 익명 함수가 직접 호출하는 외부 함수의 값입니다 i. 루프가 끝나면 i=10해제되어야 하는 수명 주기가 i확장됩니다. 대리자를 실행하면 익명 함수가 따라서 this i와 이번에는 i=1010 곱하기 10이 출력됩니다.

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

하지만 int index = i위의 함수는 값형 변수이기 때문에 이를 사용하면 구현이 가능하며, for 루프에서 사용할 때마다 int실제로 index는 새로운 int변수를 선언하게 됩니다. 따라서 각 익명 함수 호출에 대한 인덱스는 실제로 10개의 서로 다른 인덱스입니다.

람다 식이 실수로 외부 함수의 upvalue를 캡처하는 것이 걱정된다면 static키워드를 사용하여 이를 제한할 수 있습니다.

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

추천

출처blog.csdn.net/milu_ELK/article/details/132415187