예전 포스트에서 콜백 개념과 함께 한번 다뤘었지만, 다시 한번 정리해봤습니다.

delegate
델리게이트, 우리말로 대리자. (야 대리불러~ )
메서드 시그니처 (매개변수나 반환형) 가 같은 메서드를 가리키면서,
나중에 다른 클래스에서 크 메서드를 대신 호출할 수 있게 해주는 타입입니다.
Callback 콜백 함수, 이벤트 처리 등에서 많이 사용합니다.
delegate 는 참조 타입 객체입니다. 그렇기에 생성 시 할당이 됩니다.
쉽게 생각해, 변수에다가 숫자나 문자열을 담듯이,
메서드를 담는 변수라고 생각해봅시다.
선언은 아래 처럼 할 수 있습니다.
public delegate void Notifier(string message);
// string 을 매개인자로, 리턴 없는 void 메서드를 담을 수 있는 타입을 만들었어용
등록해 호출하기 위해서는, 매개변수와 반환형이 같은지 늘 확인해야 합니다.
void ConsoleNotify(string msg) => Console.WriteLine(msg);
// 메서드를 Notifier 타입의 n 에 대입합니다요
Notifier n = ConsoleNotify;
아래와 같이 필요한 시점에서 (다른 클래스에서 대신 실행) 호출할 수 있습니다.
string str = "I luv U~";
n(str);
// 아래와 같은 행동입니당
ConsoleNotify(str);
delegate - null 확인
할당 된 콜백 메서드 (Subscriber 라고도 부르나 봐요) 가 없을 수도 있으니,
늘 null 을 호출하지는 않는지 체크해 안전하게 호출하도록 해야합니다.
Notifier n = null;
// 제일 간단하고 보기 좋지요
if (n != null)
n("hi");
// 널라블 nullable 로 확인
n?.Invoke("hi");
// 이벤트 패턴, 스레드 안정성을 위해 로칼복사본으로 호출해용
var temp = n;
temp?.Invoke("hi");
Action / Func / Predicate
이 세개는 비슷비슷하기 때문에 한꺼번에 정리하도록 하겠습니다.
Action, void 라서 리턴값이 없는 콜백이기 때문에 그냥 실행만 하면 된다, 싶으면 사용합니다.
Action a0; // 매개변수 0개, void 반환형
Action<int> a1; // int 1개, void
Action<int, string> a2; // int, string 로 매개변수가 2개인 void 타입
사실 아래와 똑같습니다.
delegate void Action(); // 매개변수 없음
delegate void Action<T>(T n); // 매개변수 1개
delegate void Action<T1, T2>(T1 a, T2 b); // 매개변수 2개
Func, 반환값이 있는 콜백으로 결과값을 돌려 받아야 한다면 사용합니다.
Func 는 <T1, T2, ... , Tn> 에서 마지막 인자가 반환형입니다.
Func<int> f0; // 매개변수 0개, 하지만 반환형은 int
Func<int, bool> f1; // int 넣어서 -> bool 로 반환
Func<string, int, float> f2; // 매개변수 2개 (string, int) -> float 반환
Predicate<T>, bool 리턴값으로 그냥 Func<T, bool> 과 다를 바가 없음
Predicate<int> p; // int -> bool, 그냥 Func<int, bool> 랑 똑같아용
List 정렬에 쓰이는 Comparison<T> 같은 경우는 Func<T, T, int> 와 똑같지만,
의도가 분명하니 사용되는 편입니다.
추가)
아래 몇가지 예재를 정리했습니다.
아래는 기본적인 콜백 사용 예시입니다.
public void OnClick(Action callback)
{
// 어쩌구 저쩌구 코드들 수행 후
callback?.Invoke(); // nullable 콜백
}
// 사용하고자 하는 곳에서 아래처럼
OnClick(() => Debug.Log("Clicked"));
아래처럼 생성 직후에 초기화하는 방법도 쓰입니다.
public T Create<T>(Action<T> init = null) where T : new()
{
T obj = new T();
init?.Invoke(obj); // 만든 직후, 넘겨준 초기화 로직 실행
return obj;
}
// 아래처럼
Player player = Create<Player>(p => { p.Name = "pee"; p.Hp = 100; });
조건을 확인하는 용도로도 Predicate 를 사용할 수 있습니다.
List<int> nums = new(){ 1, 2, 3, 4, 5 };
Predicate<int> isEven = x => x % 2 == 0; // 짝수놈들만
int firstEven = nums.Find(isEven); // 2, 첫번째 짝수
var evens = nums.FindAll(x => x % 2 == 0); // [2,4] 모든 짝수들
Func<TIn, TOut> 이런식으로 작업할 수도 있습니다.
Func<int, string> toText = n => $"#{n}";
string s = toText(7); // "#7"
코딩하다보면 상당히 자주 쓰이는 구문(시그니처) 들이므로 눈에 익혀두면 좋습니다.
그럼 오늘도 좋은 하루 보내세요~
안뇽 ~ (˶˃ᆺ˂˶)