I'm FanJae.

[20260515] C# ( Delegate, 대리자 ) 본문

Unity/Unity 초격차캠프

[20260515] C# ( Delegate, 대리자 )

FanJae 2026. 5. 15. 18:30

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
Comments