
C#에서 대리자는 메서드를 변수처럼 다루는 강력한 기능입니다.
만약 한 명의 심부름꾼에게 여러 개의 임무를 순서대로 시키고 싶다면
어떻게 해야 할까요? 예를 들어, 버튼 하나를 클릭했을 때 로그도 남기고,
파일도 저장하고, 화면도 갱신하는 것처럼요. 이럴 때 C#은 여러 개의 메서드를
체인처럼 연결하여 순차적으로 호출하는 강력한 기능을 가지고 있습니다.
이를 멀티캐스트 대리자(Multicast Delegate)라고 부릅니다.
멀티캐스트 대리자(Multicast Delegate)는 하나의 대리자 인스턴스가
여러 개의 메서드를 참조하는 목록(Invocation List)을 가지는 것을 말합니다.
한 번 호출하면, 연결된 모든 메서드가 목록에 추가된 순서대로 실행됩니다.
C#에서 System.MulticastDelegate클래스에서 파생된
모든 대리자 타입(delegate, Func, Action모두 포함)은
이 멀티캐스트 기능을 기본적으로 가지고 있습니다.
기존의 대리자가 "우유 사 오기"라는 단일 임무 쪽지였다면,
멀티캐스트 대리자는 아래와 같이 여러 임무가 적힌 '체크리스트'와 같습니다.
[오늘의 심부름 목록]
1. 은행 가서 입금하기
2. 세탁소 가서 옷 찾아오기
3. 마트 가서 계란 사 오기
우리는 심부름꾼에게 이 목록 하나만 전달하면,
심부름꾼은 1번부터 3번까지 순서대로 모든 임무를 수행합니다.
대리자는 +와 - (또는 +=, -=) 연산자를 사용하여 메서드 목록을 쉽게 조작할 수 있습니다.
먼저, 여러 가지 알림(Notification) 기능을 수행하는 메서드들을 준비해 보겠습니다.
[코드]
public class Notifier
{
public void SendEmail(string message)
{
Console.WriteLine($"[이메일 발송] {message}");
}
public void SendSms(string message)
{
Console.WriteLine($"[SMS 발송] {message}");
}
public void LogToFile(string message)
{
Console.WriteLine($"[파일 로깅] {message}");
}
}
+= 연산자로 메서드 연결이제 Action<string>타입의 대리자 하나에 위 메서드들을 차례대로 연결해 보겠습니다.
[코드]
class Program
{
static void Main()
{
Notifier notifier = new Notifier();
// 1. 빈 대리자 인스턴스를 생성
Action<string> notificationChain = null;
// 2. += 연산자를 사용하여 메서드를 하나씩 체인에 추가
notificationChain += notifier.SendEmail;
notificationChain += notifier.SendSms;
notificationChain += notifier.LogToFile;
// 3. 대리자를 단 한 번만 호출!
Console.WriteLine("--- 알림 발송 시작 ---");
notificationChain("긴급 서버 점검이 있습니다.");
}
}
[실행 결과]
--- 알림 발송 시작 ---
[이메일 발송] 긴급 서버 점검이 있습니다.
[SMS 발송] 긴급 서버 점검이 있습니다.
[파일 로깅] 긴급 서버 점검이 있습니다.
notificationChain을 한 번 호출했을 뿐인데,
연결된 세 개의 메서드가 모두 순서대로 실행되었습니다.
-= 연산자로 메서드 제거이번에는 연결된 체인에서 특정 메서드를 제거해 보겠습니다.
예를 들어, "이제 SMS는 보내지 않는다."라고 결정한 상황입니다.
[코드]
class Program
{
static void Main()
{
Notifier notifier = new Notifier();
// 1. 빈 대리자 인스턴스를 생성
Action<string> notificationChain = null;
// 2. += 연산자를 사용하여 메서드를 하나씩 체인에 추가
notificationChain += notifier.SendEmail;
notificationChain += notifier.SendSms;
notificationChain += notifier.LogToFile;
// 3. -= 연산자를 사용하여 SendSms 메서드를 체인에서 제거
notificationChain -= notifier.SendSms;
// 4. 다시 대리자를 호출
Console.WriteLine("--- SMS 제외 후 알림 발송 시작 ---");
notificationChain("점검이 완료되었습니다.");
}
}
[실행 결과]
--- SMS 제외 후 알림 발송 시작 ---
[이메일 발송] 점검이 완료되었습니다.
[파일 로깅] 점검이 완료되었습니다.
SendSms메서드가 성공적으로 제거되어 더 이상 호출되지 않는 것을 확인할 수 있습니다.
멀티캐스트 대리자는 매우 강력하지만, 반환 값이 있는
대리자(Func등)와 함께 사용할 때 주의해야 할 점이 있습니다.
만약 반환 값이 있는 메서드들이 체인으로 연결되어 있다면,
최종 반환 값은 가장 마지막에 호출된 메서드의 반환 값만 남게 됩니다.
이전 메서드들의 반환 값은 모두 무시됩니다.
[코드]
Func<int, int> calcChain = null;
calcChain += FirstCalc;
calcChain += SecondCalc;
int result = calcChain(10);
Console.WriteLine($"최종 결과: {result}");
int FirstCalc(int x)
{
Console.WriteLine("첫 번째: x + 1");
return x + 1;
}
int SecondCalc(int x)
{
Console.WriteLine("두 번째: x * 2");
return x * 2;
}
[실행 결과]
첫 번째: x + 1
두 번째: x * 2
최종 결과: 20
첫 번째 메서드가 반환한 11은 사라지고, 마지막 메서드가
반환한 20만이 최종 결과가 되었습니다. 따라서 멀티캐스트 대리자는
주로 반환 값이 없는 Action계열 대리자와 함께 사용하여
'일련의 동작'을 수행하는 데 사용하는 것이 일반적입니다.
멀티캐스트 대리자(Multicast Delegate)는 하나의 호출로 여러 동작을
연쇄적으로 실행할 수 있게 해주는 C#의 핵심 기능 중 하나입니다.
| 연산자 | 역할 | 설명 |
|---|---|---|
+= | 구독(Subscribe) | 대리자의 호출 목록에 새로운 메서드를 추가합니다. |
-= | 구독 취소(Unsubscribe) | 대리자의 호출 목록에서 특정 메서드를 제거합니다. |