본문 바로가기
언어(C, C++, C#)

[C#] delegate와 event

by 적용1 2024. 11. 8.
728x90

C#에서 delegate(대리자)와 event(이벤트)는 둘 다 메서드를 가리키고 실행할 수 있는 기능을 제공하는 요소지만, 사용 목적과 방식에 차이가 있다.

delegate

  • delegate는 특정 메서드의 참조를 저장하고 호출할 수 있는 타입이다. C++에서 함수 포인터와 비슷한 개념이라 생각하면 된다.
  • 파라미터와 리턴 타입을 통해 정의하게 되며 이후 리턴, 파라미터 타입이 같은 메소드들과 호환되어 이 메소드들에 대한 참조를 가질 수 있게 된다.
  • 사용 예시 : 콜백(callback) 메커니즘을 구현하거나, 특정 시점에 실행할 작업 목록을 정의할 때 유용하다.

    콜백 메커니즘 : 특정 이벤트나 작업이 완료된 후 실행될 메서드를 미리 지정하는 방식
/// 매개변수가 없는 delegate
public delegate void Notify();	// delegate 선언

public class Process
{
	public Notify? OnProcessComplete;	// delegate 인스턴스 선언(null이 될 수 있도록 허용)
    
    pbulic void StartProcess()
    {
    	Console.WriteLine("Process Started.");
        OnProcessComplete?.Invoke();	// OnProcessComplete가 null이 아니라면 참조한 메서드 호출
    }
}


/// 매개변수와 return 타입이 있는 delegate
public delegate int Operation(int x, int y);

public class Calculator
{
	// 메서드의 매개변수와 return 타입이 같음
	public int Add(int x, int y)
    {
    	return x + y;
    }
    
    public int Subtract(int x, int y)
    {
    	return x - y;
    }
}

public class Program()
{
	public static void Main()
    {
    	Calculator calculator = new Calculator();
        
        Operation operation = Calculator.Add();	// delegate 선언 및 메서드 할당
        Console.WriteLine("Add Result : " + operation(10, 5));	// 출력 : Add Result : 15
        
        operation = Calculator.Subtract;
        Console.WriteLine("Add Result : " + operation(10, 5));	// 출력 : Add Result : 5
    }
}

/// deletage를 함수 인자로 넘겨서 호출하는 경우
using System;

public delegate void Notify(string message);  // delegate 정의

public class Notifier
{
    // 델리게이트를 인자로 받아서 호출하는 메서드
    public void DoWork(Notify callback)
    {
        Console.WriteLine("Doing some work...");

        // 델리게이트 호출
        callback?.Invoke("Work completed!");  // null일 경우 안전하게 호출
    }
}

public class Program
{
    public static void Main()
    {
        Notifier notifier = new Notifier();

        // 델리게이트에 할당할 메서드 정의
        Notify notifyMethod = message => Console.WriteLine($"Callback message: {message}");

        // DoWork 메서드에 델리게이트를 인자로 전달
        notifier.DoWork(notifyMethod);  // 출력: "Doing some work..." 및 "Callback message: Work completed!"
    }
}

 

위의 예시와 같이 같은 타입의 매개변수를 받고 반환하는 delegate를 통해 함수의 참조를 넘겨서 원하는 타이밍에 호출을 하거나, delegate를 함수의 인자로 넘겨 콜백 패턴을 구현하는 등 다양한 곳에 사용할 수 있다.

event

  • event도 델리게이트와 함께 사용되는, 특정 이벤트 발생 시 어떤 메서드가 실행될지 결정하는 메커니즘이다. 하지만, 가장 큰 차이점으로는 event를 호출할 수 있는 것은 해당 event를 가진 클래스만 가능하다는 것이다.
  • 사용 예시 : Button.Click과 같은 UI 요소의 이벤트나 특정 조건에서 특정 기능을 호출하고 싶을 때 유용하다.
public class GameEvents
{
	// 반환값이 없는 이벤트(매개변수 없음)
	public event Action OnGameStart;
    
    // 두 개의 int를 받아 int를 반환하는 이벤트
    public event Func<int, int, int> OnCalculateScore;
    
    // string을 받아 bool을 반환하는 이벤트
    public event Predicate<string> OnCheckCondition;
    
    public void StartGame()
    {
    	Console.WriteLine("Game Starting...");
        OnGameStart?.Invoke();	// OnGameStart가 null이 아닌 경우에 참조한 메서드 실행
   	}
    
    public void CalculateScore(int a, int b)
    {
      	int result = OnCalculateScore?.Invoke(a, b) ?? 0;	// OnCalculateScore가 null이 아닌 경우에
        												// 참조한 메서드의 반환값을 저장
                                                        // null이라면 0을 저장
        Console.WriteLine($"Calculated Score : {result}");
    }
    
    public void CheckCondition(string input)
    {
    	bool isCondition = OnCheckCondition?.Invoke(input) ?? false;
        Console.WriteLine(%"Condition Met : {isConditionMet}");
    }
}

public class Program
{
	public static void Main()
    {
    	GameEvents gameEvents = new GameEvents();
        
        // 람다식을 통해 이벤트 구독
        gameEvents.OnGameStart += () => ConsoleWriteLine("Gmae has started!");
        gameEvents.OnCalculateScore += (a, b) => a + b;
        gameEvents.OnCheckCondition += input => input == "Win";
        
        // 이벤트 실행
        gameEvents.GameStart();
        gameEvents.CalculateScore(5, 10);
        gameEvents.CheckCondition("Win");
    }
}

 

위와 같이 이벤트 구독과 호출을 이벤트가 정의된 클래스 내에서만 할 수 있다.

Action, Func, Predicate

이 키워드들은 자주 사용하게 되는 델리게이트를 템플릿화 한 것들이다. event 선언 시 해당 타입들을 사용할 수 있다.

  • Action : 함수 파라미터가 T이고 반환값이 void인 경우 사용(최대 16개의 매개변수를 받을 수 있다.)
  • Func<T, TResult> : 함수 파라미터가 T이고 반환 타입이 TResult인 경우 사용(최대 16개의 매개변수를 받을 수 있다.)
  • Predicate : 함수 파라미터가 T이고 반환값이 bool인 경우 사용(매개변수 1개를 받을 수 있다.)

 

728x90