C# 기초부터 마스터까지 — 5화
델리게이트, 이벤트, 람다로 동작 전달하기
본 블로그 작성 글들은, 강의를 목표로 하고 제작되었습니다. 또한 작성된 글의 정보도 틀렸을 수 있으니, 주의해주세요.
메서드는 직접 호출할 수도 있지만 나중에 실행할 동작 자체를 값처럼 전달할 수도 있다. C#의 델리게이트, 이벤트, 람다는 이 흐름을 표현한다.
델리게이트
int Double(int value) => value * 2;
Func<int, int> operation = Double;
int result = operation(10);
Action<string> logger = message => Console.WriteLine(message);
logger("Battle started");
Func<int, int>는 int를 받아 int를 반환하는 동작이고, Action은 반환값이 없는 동작이다. 호출하는 코드는 실제로 어떤 메서드가 실행되는지 몰라도 된다.
이벤트
public sealed class Player
{
public event Action<int>? HealthChanged;
public int Health { get; private set; } = 100;
public void TakeDamage(int amount)
{
Health = Math.Max(0, Health - amount);
HealthChanged?.Invoke(Health);
}
}
player.HealthChanged += health => healthBar.SetValue(health);
이벤트는 상태를 가진 객체가 변화를 알리고, UI나 사운드 같은 다른 객체가 반응하게 만든다. 외부에서는 구독과 해제만 할 수 있고 이벤트를 마음대로 호출할 수 없다.
구독 수명 관리
void OnEnable()
{
player.HealthChanged += UpdateHealth;
}
void OnDisable()
{
player.HealthChanged -= UpdateHealth;
}
오래 살아 있는 객체의 이벤트에 짧게 사는 객체가 계속 구독되어 있으면 메모리와 호출이 남을 수 있다. 등록한 위치와 대응되는 해제 위치를 함께 정해야 한다.
람다 캡처
int bonus = 5;
Func<int, int> addBonus = damage => damage + bonus;
람다가 바깥 지역 변수를 사용하면 그 값을 보관하기 위한 객체가 만들어질 수 있다. 매 프레임 반복되는 코드에서 캡처 람다를 계속 만들면 불필요한 할당이 생길 수 있다.
델리게이트는 동작의 타입이고, 이벤트는 변화 알림의 경계를 만든다. 편리하다는 이유로 모든 호출을 이벤트로 바꾸면 실행 순서를 추적하기 어려워지므로 느슨한 연결이 필요한 지점에 사용해야 한다.