카테고리 없음

🌟 C# 람다 식(Lambda Expression) feat. 변수 캡처, 클로저😋 🌟

minsoungdev 2026. 7. 23. 23:44

안녕하세요. 박민성입니다. 🤗

 

오늘은 C#에 람다 식(Lambda Expression)에 대해서 글을 쓰고자 합니다. 🤔

 

저는 람다식을 평소 Unity 개발 해올때 많이 써왔습니다.

많이 써왔지만, 그냥 "이렇게 쓰는구나~"하고 사용해 왔는데요.

람다 식에 대해서 자세하고 정확히 알기 위해서 람다 식에 대해서 글을 쓰고자 합니다.

 

1. 람다식이란?

람다식은 이름이 없는 함수를 간결하게 표현하는 문법입니다. 

 

2. 람다식의 기본 구조

매개변수 => 실행할 코드
// 예시
number => number * 2

여기서 =>이 람다 연산자입니다. 

 

만약에 매개변수가 없다면

() => Debug.Log("실행");

이렇게 () 괄호를 적고 람다식을 써주면 됩니다.

 

만약에 매개변수가 여러 개면?

어떻게 할까~~~요?

(a, b) => a + b

이런 식으로 괄호 안에 콤마를 직고 매개변수를 적으면 됩니다.

 

당연히 위에서 본 매개변수 하나 들어간 버전도

(number) => number * 2

이런 식으로 괄호를 적어도 됩니다. 

 

3. 표현식 람다와 문 람다

람다를 찾아보다가 람다에도 종류가 있는걸 봤습니다. 

람다에는 표현식 람다와 문 람다가 있습니다.

 

두가지의 차이점은

한 줄로 실행하는 것이 표현식 람다라고 하고여

number => number * 2 // 표현식 람다

여러 줄을 실행하는 것을 문 람다라고 합니다.

number =>
{
    int result = number * 2;
    Debug.Log(result);
    return result;
}

문 람다를 사용하려면 람다식을 쓰고 중괄호를 쓰고 식들을 적으면 됩니다.

여기서 주의할 점은 표현식 람다는 오른쪽 식의 결과가 자동으로 반환되지만,

문 람다는 반환값이 필요하면 return을 무조건 적어야지 반환할 수 있습니다.

 

4. 델리게이트와의 관계

델리게이트는 대리자로 함수를 담아서 한꺼번에 부를 수 있는 친구입니다.

내장으로 있는 델리게이트 종류는 Action, Func, Predicate이 있습니다.

여기서 람다식은 무명"함수"이기 때문에 델리게이트에 종류에 담아서

변수 선언하여 여러 곳에서 사용할 수 있는데여

Func<int, int> doubleValue = number => number * 2;

여기서

var doubleValue = number => number * 2; // XXXXXX

이런 식으로 타입 추론(var)를 사용해서 선언하는 것이 안되는데요.

왜냐하면 컴파일러가 람다식의 대상 타입을 알 수 없기 때문입니다. (처음 알았습니다 헉)

 

5. Unity 활용 예제

button.onClick.AddListener(() => OpenPanel());

버튼 이벤트에 함수를 AddListener시킬 수 있는데요.(여기서 AddListener는 버튼 이벤트인 onClick에 발생 되었을 때 발생되었다는 것을 알려주는? onClick이 되면 AddListener한 함수 실행됨)

SkinDataSO skin = skinList.Find(skin => skin.skinId == targetId);

이렇게 리스트 검색을 할때도 유용하게 씁니다. (Find안에는 Predicate<T> 이 들어갑니다.)

 

6. 외부 변수 캡처와 클로저

람다식 밖의 변수를 내부에서 사용할 수 있습니다.

int damage = 10;

Action attack = () =>
{
    enemy.TakeDamage(damage);
};

이때 람다가 외부의 damage 변수를 사용하는 것을 변수 캡처라고 합니다.

https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/expressions?utm_source=chatgpt.com

변수 캡처란 익명 함수가 바깥 변수를 참조하면 해당 변수가 캡처된 것입니다.

 

그렇기 때문에 간단한 코드 예제입니다.

int damage = 10;

Action attack = () => Debug.Log(damage);

damage = 30;

attack(); // 30

attack을 선언할때 damage는 10입니다. 하지만 damage 값을 바꾸면

attack을 호출했을 때 그 결과 값이 달라지는데요.

보통은 변수 선언할 때 그 값을 복사합니다.

int damage = 10;

int damage2 = damage

damage = 30;

damage2 // 10

하지만 람다 생성 시에 그 값을 복사하는 것이 아닌 그 변수를 갭처함으로서 변수를 공유합니다.

이것이 람다식 변수 캡처입니다.

 

여기서 클로저가 안나올 수가 없는데요.

클로저란 람다식과 그 람다가 캡처한 외부 변수들을 함께 유지하는 구조입니다.

컴파일러는 개념적으로 다음 코드가 있을 때

int damage = 10;
Action attack = () => Debug.Log(damage);

캡처한 변수를 보관할 숨겨진 객체를 만들어서 처리합니다.

class GeneratedClosure
{
    public int damage;

    public void Attack()
    {
        Debug.Log(damage);
    }
}

이런식으로?

 

여기서. 함수 안에서 사용하는 것이 아닌 클래스 필드에 있다면????

private int damage = 10;

private void Start()
{
    Action attack = () => Debug.Log(damage);
}

이 경우에는 람다가 damage를 캡처한 것이 아닌 현재 객체에 this를 통해서 필드를 접근합니다.

여기서 중요한점!!🌟🌟🌟🌟🌟

만약에 위에처럼 함수 안에서 damage를 사용해서 변수 캡처가 일어나면

그 캡처한 람다를 담는 클로저 객체가 생기는데요!!

반복적으로 이 클로저 객체를 생성하게 된다면 이 클로저는 class이기 때문에 메모리 할당 발생합니다.

그러면 GC가 처리해야 하기 때문에 반복적인(예: Update())에서 새 람다를 만드는 것은 피하는 게 좋습니다.

private void Update()
{
    int damage = 10;

    Action attack = () => enemy.TakeDamage(damage);
    attack();
} // X X X X X X X X X X X X X X X X X

 

이상으로 람다 식에 대해서 알아보았는데요~!!!!

오늘은 찾아보고 또 찾아보면서 모르는 내용이 계속 나와서 머리가 아팠습니다. 🤕

하지만 정말 신기하고 새로운 지식들이 머리에 들어와서 정말 재밌고 알찬 하루가 되었던 것 같습니다. ☺️

 

내일은 더욱 알차게 보내고 작성해보도록 하겠습니다. 

봐주셔서 감사합니다. ☺️