본문 바로가기
개발💻/C#

[C#] Delegate 1

by Sports Entrepreneur 2024. 6. 8.

 델리게이트란 

델리게이트는 특정 메서드에 대한 참조를 나타내는 타입 안전한 객체입니다. 이는 C#에서 함수 포인터와 유사하게 작동하며, 한 개 이상의 메서드를 호출할 수 있도록 합니다. 델리게이트는 다음과 같은 상황에서 유용하게 사용됩니다.

 

1. 이벤트와 콜백 메서드 : 델리게이트는 이벤트와 콜백 메서드를 구현하는 기본적인 수단입니다.

2. 메서드 참조 저장 : 메서드의 참조를 저장하고 실행할 수 있게 합니다.

3. 동적 메서드 호출 : 런타임에 메서드를 동적으로 호출할 수 있습니다.

 

델리게이트의 종류 

델리게이트 유형 설명
사용자 정의 델리게이트 특정 메서드 시그니처를 직접 정의하여 사용.
Action 델리게이트 반환값이 없는 메서드를 참조.
Func 델리게이트 반환값이 있는 메서드를 참조.
Predicate 델리게이트 반환 타입이 bool인 메서드를 참조하여 조건 검사를 수행.
멀티캐스트 델리게이트 여러 메서드를 참조할 수 있는 델리게이트.
EventHandler 델리게이트 이벤트와 함께 사용되는 표준 델리게이트.
EventHandler<TEventArgs> 델리게이트 사용자 정의 이벤트 인자를 받을 수 있는 제네릭 버전의 이벤트 핸들러 델리게이트.

 

1. 사용자 정의 델리게이트

사용자가 직접 정의하는 델리게이트로, 특정 메서드 시그니처를 가리킬 수 있습니다.

    // 반환값이 없고, string 매개변수를 받는 델리게이트
    public delegate void JamesDelegate(string message);

    // float 를 반환하고, 두개의 float 매개변수를 받는 델리게이트
    public delegate float CalculateJamesWeightDelegate(float before, float after);

 

2. 'Action' 델리게이트

'Action' 델리게이트는 반환값이 없는 메서드를 참조할 때 사용됩니다. 매개변수의 개수와 타입을 제네릭으로 지정할 수 있습니다.

// 매개변수가 없는 Action
Action action = () => Debug.Log("Hello, James!");

// 하나의 매개변수르 받는 Action
Action<string> greet = (name) => Debug.Log($"Hello, {name}");

// 두 개의 매개변수를 받는 Action
Action<float, float> addWeight = (before, after) => Debug.Log(before + after);

 

3. 'Func' 델리게이트

'Func' 델리게이트는 반환값이 있는 메서드를 참조할 때 사용됩니다. 마지막 제네릭 매개변수가 반환 타입을 나타내며, 그 이전의 매개변수들은 메서드의 매개변수를 나타냅니다.

// 반환값이 없는 Func
Func<int> getRandomNumber = () => new Random().Next(1, 100);

// 하나의 매개변수를 받고, 반환값이 있는 Func
Func<string, int> getLength = (str) => str.Length;

// 두 개의 매개변수를 받고, 반환값이 있는 Func
Func<int, int, int> multiply = (a, b) => a * b;

 

4. 'Predicate' 델리게이트

'Predicate' 델리게이트는 특정 조건을 검사하는 메서드를 참조할 때 사용됩니다. 반환 타입은 항상 'bool'이며, 하나의 매개변수를 받습니다.

// 하나의 int 매개변수를 받아 조건을 검사하는 Predicate
Predicate<int> isEven = (n) => n % 2 == 0;

 

5. 멀티캐스트 델리게이트

멀티캐스트 델리게이트는 여러 개의 메서드를 참조할 수 있습니다. 델리게이트에 '+=' 연산자를 사용하여 메서드를 추가하고, '-=' 연산자로 메서드를 제거할 수 있습니다.

public delegate void Notify(string message);

public class Program
{
    public static void Main(string[] args)
    {
        Notify notify = ShowMessage;
        // += 연산자를 사용
        notify += LogMessage;

        notify?.Invoke("Process Completed.");
        // -= 연산자를 사용
        notify -= ShowMessage;
        notify?.Invoke("Process Completed Again.");
    }

    public static void ShowMessage(string message)
    {
        Console.WriteLine("Show: " + message);
    }

    public static void LogMessage(string message)
    {
        Console.WriteLine("Log: " + message);
    }
}

 

6. 'EventHandler' 델리게이트

이벤트와 함께 사용되는 표준 델리게이트로, 매개변수가 두 개(object sender, EventArgs e)인 메서드를 참조합니다.

public class Program
{
    public static event EventHandler MyEvent;

    public static void Main(string[] args)
    {
        MyEvent += OnMyEvent;
        MyEvent?.Invoke(null, EventArgs.Empty);
    }

    public static void OnMyEvent(object sender, EventArgs e)
    {
        Console.WriteLine("Event triggered!");
    }
}

 

7. EventHandler<TEventArgs> 델리게이트
일반적인 이벤트 핸들러 델리게이트의 제네릭 버전으로, 사용자 정의 이벤트 인자를 받을 수 있습니다.

public class MyEventArgs : EventArgs
{
    public string Message { get; set; }
}

public class Program
{
    public static event EventHandler<MyEventArgs> MyEvent;

    public static void Main(string[] args)
    {
        MyEvent += OnMyEvent;
        MyEvent?.Invoke(null, new MyEventArgs { Message = "Hello, Event!" });
    }

    public static void OnMyEvent(object sender, MyEventArgs e)
    {
        Console.WriteLine(e.Message);
    }
}

 

 

Unity 이벤트와 C# 델리게이트의 차이점 

특징 Unity 이벤트 C# 델리게이트
설정 및 관리 인스펙터에서 설정 및 관리 가능 코드에서 설정 및 관리
사용 편의성 비프로그래머도 인스펙터를 통해 설정 가능 프로그래머가 코드로 설정해야 함
성능 약간의 오버헤드 존재 상대적으로 높은 성능
확장성 Unity의 다른 시스템과 잘 통합됨 일반적이고 강력한 기능 제공
타입 안전성 제네릭을 사용하여 다양한 타입 처리 가능 타입 안전성 보장
이벤트 핸들러 추가 인스펙터 또는 코드에서 AddListener로 추가 코드에서 += 연산자로 추가
이벤트 핸들러 제거 인스펙터 또는 코드에서 RemoveListener로 제거 코드에서 -= 연산자로 제거
멀티캐스트 지원 지원 지원
주 사용 용도 Unity 게임 오브젝트 간의 이벤트 처리 일반적인 C# 이벤트 처리
주요 장점 직관적이고 사용하기 쉬움 높은 성능과 유연성 제공
주요 단점 성능 오버헤드 및 인스펙터 의존성 설정 및 관리가 코드로만 가능

 

 

  • C# 델리게이트는 더 높은 성능과 유연성을 제공하며, 코드를 통해 직접 제어할 수 있는 강력한 기능을 제공합니다.
  • UnityEvent는 인스펙터에서 쉽게 설정하고 관리할 수 있어, 게임 디자이너나 아티스트가 직접 이벤트를 설정할 수 있는 장점이 있습니다.

 

 

Unity 이벤트 예시

using UnityEngine;
using UnityEngine.Events;

public class Player : MonoBehaviour
{
    public UnityEvent onJamesEat;

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            Eat();
        }
    }

    void Eat()
    {
        Debug.Log("James Eat");
        onJamesEat?.Invoke();
    }
}

public class GameManager : MonoBehaviour
{
    public Player player;

    void Start()
    {
        player.onJamesEat.AddListener(OnJamesEat);
    }

    void OnJamesEat()
    {
        Debug.Log("Gain Weight!");
    }
}

 

 

C# 델리게이트 예시

using System;

public class Process
{
    public delegate void ProcessCompletedEventHandler(string message);
    public event ProcessCompletedEventHandler ProcessCompleted;

    public void StartProcess()
    {
        Console.WriteLine("Process Started.");
        OnProcessCompleted("Process Completed.");
    }

    protected virtual void OnProcessCompleted(string message)
    {
        ProcessCompleted?.Invoke(message);
    }
}

public class Program
{
    public static void Main(string[] args)
    {
        Process process = new Process();
        process.ProcessCompleted += ProcessCompletedHandler;
        process.StartProcess();
    }

    public static void ProcessCompletedHandler(string message)
    {
        Console.WriteLine(message);
    }
}

 

 

성능 테스트

using System.Diagnostics;
using UnityEngine;
using UnityEngine.Events;
using Debug = UnityEngine.Debug;

public class ComparePerformance : MonoBehaviour
{
    // 델리게이트 선언
    public delegate void MyDelegate();
    public event MyDelegate MyDelegateEvent;

    // UnityEvent 선언
    public UnityEvent MyUnityEvent = new UnityEvent();

    private const int TestIterations = 1000000;

    void Start()
    {
        // 델리게이트에 메서드 등록
        MyDelegateEvent += DelegateMethod;

        // UnityEvent에 메서드 등록
        MyUnityEvent.AddListener(UnityEventMethod);

        // 델리게이트 성능 테스트
        var delegateStopwatch = Stopwatch.StartNew();
        for (int i = 0; i < TestIterations; i++)
        {
            MyDelegateEvent?.Invoke();
        }
        delegateStopwatch.Stop();
        Debug.Log($"Delegate Time: {delegateStopwatch.ElapsedMilliseconds} ms");

        // UnityEvent 성능 테스트
        var unityEventStopwatch = Stopwatch.StartNew();
        for (int i = 0; i < TestIterations; i++)
        {
            MyUnityEvent?.Invoke();
        }
        unityEventStopwatch.Stop();
        Debug.Log($"UnityEvent Time: {unityEventStopwatch.ElapsedMilliseconds} ms");
    }

    void DelegateMethod()
    {
        // 빈 메서드
    }

    void UnityEventMethod()
    {
        // 빈 메서드
    }
}

 

결과

 

'개발💻 > C#' 카테고리의 다른 글

[C#] Coupling, Cohesion 1  (0) 2024.08.10
[C#] Observer Pattern 1  (0) 2024.07.13
[C#] Delegate 2  (0) 2024.06.24
[C#] Using 지시문  (2) 2022.08.24
[C#] Namespace  (0) 2022.08.23