| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
- Unity
- Data Structure
- git
- Toy Project
- 독서
- c#
- C++
- multi-thread
- Online Judge
- BOJ
- Network Programming
- System Programming
- PS
- Today
- Total
I'm FanJae.
[20260515] C# ( Delegate, 대리자 ) 본문
1. 정의
- 대리자(Delegate)는 메서드를 저장할 수 있는 타입이다.
- 쉽게 말하면 메서드를 담는 변수라고 볼 수 있다.
- 대리자에 저장할 수 있는 메서드는 매개변수 목록과 반환 타입이 대리자 선언과 맞아야 한다. 즉, 아무 메서드나 담을 수 있는 것이 아니다.
public delegate float Calculate(float x,float y);
- 위 대리자는 아래 형태의 메서드만 저장이 가능하다.
float Calc(float, float) // 반환형이 float, 매개 변수 2개도 float로 대리자와 일치한다.
- 위 Calc()는 매개 변수 2개가 float로 대리자와 같고, 반환형도 float와 같다.
2. 필요한 이유
- 일반적으로 메서드는 직접 호출한다.
float result = Plus(10, 5);
- 상황에 따라 실행할 메서드를 바꿀때 조건문을 통해 호출한다.
if (type == "Plus")
{
Plus(a,b);
}
else if(type == "Minus")
{
Minus(a, b);
}
- 기능이 많아지면, 조건문이 길어지고, 코드가 복잡해 질 가능성이 크다.
- 이때 Delegate를 사용하면, 실행할 메서드 자체를 변수처럼 저장하고 교체가 가능하다.
Calculate calculate = Plus;
float result = calculate(10, 5);
- 여기서 calculate는 일반 변수 처럼 보이지만, 실제로는 Plus 메서드를 저장하고 있는 Delegate 변수이다.
Calculate calculate = Plus;
float result = calculate(10, 5); // calculate 안에 저장돤 plus 메서드 호출
- 그래서 이를 실행하면, calculate 안에 저장된 Plus 메서드를 호출한다.
3. 적용하지 않을 때 발생하는 문제
- Delegate를 사용하지 않으면 실행할 동작을 바꾸기 위해 조건문이나 분기문이 많아질 수 있다.
if (operation == "Plus") result = Plus(a,b);
else if (operation == "Minus") result = Minus(a,b);
else if (operation == "Multi") result = Multi(a,b);
else if (operation == "Divide") result = Divide(a,b);
- 기능이 추가될수록 조건문이 길어진다.
- 메서드를 선택하는 코드와 실행 코드가 강하게 묶인다. → 즉, 메서드를 선택하는 로직과 실제 실행 로직이 한 코드 안에 붙어 있어서 서로 쉽게 분리하기 어려워진다.
- 콜백, 이벤트 처리, 정렬 기준 전달 같은 구조를 만들기 어려워진다.
- 같은 흐름에서 실행할 동작만 바꾸고 싶은 경우 코드가 지저분해진다.
4. 올바른 적용법 예시
internal class Program
{
public delegate float Calculate(float x, float y);
public static float Plus(float left, float right)
{
return left + right;
}
public static float Minus(float left, float right)
{
return left - right;
}
public static float Multi(float left, float right)
{
return left * right;
}
public static float Divide(float left, float right)
{
return left / right;
}
static void Main(string [] args)
{
Calculate calculate = Plus;
Console.WriteLine(calculate(10, 5));
calculate = Minus;
Console.WriteLine(calculate(10, 5));
}
}
- Calculate calculate = Plus; 이 문장은 Plus 메서드를 calculate 변수에 저장한 것이다.
- calculate(10, 5) 처럼 호출할 수 있다.
- 직접, Plus(10, 5)를 호출하는 것이 아닌, 대리자 변수에 담긴 메서드를 호출하는 방식이라는 점에 있다.
① void 반환 대리자
public delegate void MessageDelegate(string message);
public static void Message(string str)
{
Console.WriteLine(str);
}
static void Main(string [] args)
{
MesageDelegate messageDelegate = Message;
messageDeletegate("Hello Delegate");
}
void 메서드이름(string)
- 위에서 선언된 MessageDelegate(string message)는 void function Name(string) 과 같은 형태의 메서드만 저장할 수 있다.
② 간략한 표현 방식
- 아래처럼 명시적으로 생성하거나, 간단하게 작성할 수 있다.
Calculate calculate = new Calculate(Plus);
Calculate calculate = Plus;
③ 멀티캐스트 대리자
public delegate void Notify();
public static void PlaySound()
{
Console.WriteLine("사운드 재생");
}
public static void ShowMessage()
{
Console.WriteLine("메시지 출력");
}
static void Main()
{
Notify notify = null;
notify += PlaySound;
notify += ShowMessage;
notify?.Invoke();
}
- 대리자는 하나의 메서드만 저장할 수도 있지만, += 연산자를 사용하면 여러 메서드를 연결하는 것도 가능하다.
- 이렇게 여러 메서드가 연결된 대리자를 멀티캐스트 대리자라고 한다.
- 여기서 Invoke는 대리자 안에 저장된 메서드를 실행하라는 의미다. 위 코드는 notify가 null이 아닐 때, notify에 연결되어 있는 메서드들을 실행한다는 의미가 되는 것이다.
- notify?.Invoke()를 호출하면 PlaySound와 ShowMessage가 순서대로 실행된다.
5. 장점
- 메서드를 변수처럼 저장하고 전달이 가능하다.
- 실행할 메서드를 런타임에 바꾸는 것이 가능하다.
- 콜백 구조를 만들 수 있다. (이 내용에 대해서는 추후 콜백 함수에 대해서 배울 때 자세히 다뤄보려고 한다.)
- 이벤트 처리 방식의 기반이 된다. (이벤트에 대한 얘기는 별도 항목으로 다룬다.)
- 조건문을 줄이고 코드 구조를 분리할 수 있다.
- 메서드의 형태가 맞는지 컴파일 타임에 검사할 수 있기 때문에 타입 안정성이 좋다. 대리자는 C/C++에서 제공하는 함수 포인터와 유사하지만 타입 안전한 방식으로 메서드를 캡슐화 할 수 있다.
6. 단점 및 주의점
- 메서드를 변수에 담는다는 개념이 직관적이지 않기 때문에 처음 볼 때 구조를 이해하기 어렵다.
- 대리자에 들어가는 메서드는 반환 타입과 매개변수 형태가 맞아야 한다.
- 단순한 상황에서 사용하면 코드가 복잡해지기 때문에, 단순한 처리에서는 사용이 권장되지 않는다.
- 여러 메서드를 연결하는 멀티 캐스트 대리자의 경우 실행 순서와 반환값 처리에 주의 해야 한다. 멀티 캐스트 대리자는 여러 메서드를 하나의 대리자에 연결할 수 있고, 호출 시 등록된 순서대로 실행된다.
7. 정리
- 대리자는 메서드를 저장할 수 있는 타입이다.
- 메서드를 변수처럼 다루고 싶을 때 사용할 수 있다.
- 대리자에 저장할 메서드는 반환 타입과 매개변수 형태가 일치해야 한다.
- 콜백, 이벤트, 동작 교체, 메서드 전달 등에 사용된다.
- 단순히 메서드를 직접 호출하는 방식보다 구조를 유연하게 만들 수 있다.
'Unity > Unity 초격차캠프' 카테고리의 다른 글
| [20260515] C# ( Func ) (0) | 2026.05.15 |
|---|---|
| [20260515] C# ( Action ) (0) | 2026.05.15 |
| [20260514] C# ( HashSet<T> ) (0) | 2026.05.14 |
| [20260514] C# ( Queue<T> ) (0) | 2026.05.14 |
| [20260514] C# ( Stack<T> ) (0) | 2026.05.14 |