말감로그

24.08.26 Unity_C# - delegate, event , action 본문

TIL

24.08.26 Unity_C# - delegate, event , action

habbn 2024. 8. 26. 18:14
728x90

대리자란?

대리자는 매개 변수와 반환 형식이 정해져 있으면, 그 메서드들을 참조할 수 있게 해주는 형식을 말한다.

 

C#의 대리자의 특징은

- 함수의 포인터가 아닌 대리자 개념을 통해 메서드를 호출한다.

- 동일한 형(매개변수/리턴 타입)을 가진 메서드들을 대리자로 묶어서 관리하고, 한 번에 호출한다.

- 대리자 타입을 통해 함수의 매개 변수로도 전달이 가능하다.

 

C# 대리자의 종류

delegate

delegate는 특정 메서드를 참조할 수 있는 "포인터" 다. C언어나 C++에서 함수 포인터와 유사한 개념이다.

델리게이트를 사용하면 메서드를 변수처럼 다룰 수있어서, 특정 상황에서 어떤 메서드를 호출할 지 동적으로 결정할 수 있다.

사용할 때는 먼저 델리게이트 형식을 만들고, 그 형식을 객체로 선언한 다음, 메서드와 그 객체를 연결시키는 식으로 해야 한다.

함수 여러개를 동시에 실행시키고 싶을 때 delegate를 쓴다. 

delegate의 장점은 복잡한 코드와 복잡한 함수의 연결을 좀 더 간단하게 처리할 수 있다.

// 반환형이 void이고 파라미터가 없는 메서드를 가리킬 수 있는 델리게이트 정의
public delegate void ActionDelegate(); 

public class Player : MonoBehaviour
{
    public ActionDelegate onAction; // 델리게이트 타입의 변수

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            onAction?.Invoke(); // onAction이 설정되어 있다면 호출
        }
    }
}

 

 

Event

이벤트는 주로 어떤 일이 발생했을 때 그에 반응하는 여러 메서드를 호출할 때 사용된다. 

이벤트는 델리게이트를 기반으로 하며, "발생자(버튼)" 와 "구독자(UI업데이트, 사운드 재생 등)" 간의 통신을 가능하게 한다.

 

플레이어가 데미지를 입었을 때 이를 다른 시스템에 알리는 예제

public class Player : MonoBehaviour
{
    public delegate void HealthChangedDelegate(float newHealth);
    public event HealthChangedDelegate OnHealthChanged;

    private float health = 100f;

    void TakeDamage(float damage)
    {
        health -= damage;
        OnHealthChanged?.Invoke(health); // 건강 상태가 바뀌면 이벤트 발생
    }
}

public class UIManager : MonoBehaviour
{
    public Player player;

    void OnEnable()
    {
        player.OnHealthChanged += UpdateHealthUI; // 이벤트 구독
    }

    void OnDisable()
    {
        player.OnHealthChanged -= UpdateHealthUI; // 이벤트 구독 해제
    }

    void UpdateHealthUI(float newHealth)
    {
        Debug.Log("Health updated: " + newHealth); // UI 갱신
    }
}

 

delegate와 event 의 차이

delegate public 한정자로 선언하면 클래스 외부에서 호출이 가능합니다. 클래스 외부에서도 델리게이트에 저장된 메서드를 호출하거나 덮어쓸 수 있음을 의미합니다.

event public 한정자로 선언해도 클래스 외부에서 호출이 불가능합니다. 오직 그 이벤트를 정의한 클래스 내부에서만 호출 가능.

외부에서는 이벤트를 구독("+=") 하거나 구독 해제("-=")만 할 수 있습니다.

event객체 상태 변화나 사건 발생을 알리는 용도로 사용되고 delegatecallback의 용도로 사용됩니다. 


왜 이런 차이가 있을까?  (Feat.ChatGPT)

1. 캡슐화

델리게이트는 외부에서 직접 접근 가능한 메서드 포인터와 같습니다. 외부에서 이를 자유롭게 호출하고 수정할 수 있습니다. 하지만 이렇게 하면 프로그램이 예상하지 못한 방식으로 동작할 수 있습니다.

이벤트는 델리게이트에 대한 외부 접근을 제한함으로써, 클래스 내부에서만 그 이벤트를 발생시킬 수 있도록 합니다. 이는 캡슐화의 원칙을 따르는 것입니다. 클래스 외부에서 내부의 상태나 동작을 통제하지 못하도록 하기 위함입니다.

 

2. 안정성

이벤트를 통해 특정 상황에서만 메서드를 호출하도록 하여 프로그램의 안정성을 보장할 수 있습니다. 외부에서 무분별하게 이벤트를 호출할 수 없기 때문에, 이벤트를 발생시키는 로직이 클래스 내부에만 존재하게 됩니다.

반면, 델리게이트는 외부에서 무분별하게 호출될 수 있으므로, 이를 사용하는 프로그램이 예상치 못한 방식으로 작동할 위험이 있습니다.

 

3. 명확한 의도

델리게이트는 콜백을 설정하거나, 메서드를 다른 메서드로 전달할 때 사용됩니다.

이벤트는 클래스의 특정 상황(버튼 클릭, 상태 변경 등)에서만 발생하도록 설계된 동작입니다. 외부에서는 그 상황에 반응할 수 있지만, 그 상황을 직접 발생시키지는 못합니다. 이는 코드의 의도를 명확하게 하고, 이벤트를 언제, 왜 발생시켜야 하는지에 대한 제어를 클래스 내부에서만 가능하도록 합니다.

 

결론

Delegate : 외부에서 직접 호출할 수 있어 유연하지만, 외부에서의 통제가 가능하기 때문에 잘못 사용될 경우 프로그램이 예기치 않게 동작할 위험이 있습니다.

Event : 클래스 내부에서만 호출할 수 있기 때문에, 외부에서 해당 이벤트를 발생시키는 상황을 통제할 수 있습니다. 이는 프로그램의 안정성과 코드의 명확성을 보장하기 위한 중요한 메커니즘입니다.


Action

C#에서 기본 제공하는 델리게이트 타입으로, 메서드의 반환형이 'void'일때 사용된다. 

파라미터가 없는 Action도 있고, 최대 16개까지의 파라미터를 받을 수 있는 Action이 있다.

 

Action, Func, Event 차이

Action : Action은 매개변수를 받지만 반환값이 없는 델리게이트입니다. 보통 void 형식의 메서드나 함수를 참조하는 데 사용됩니다. 

 

Func : Func는 매개변수를 받고 반환값이 있는 델리게이트입니다. Func는 마지막 매개변수가 반환값인 형식으로 사용됩니다. Func<int,string>은 정수형 매개변수를 받아 문자열을 반환하는 메서드를 참조합니다.

 

Event : Event는 클래스 내에서 발생한 액션을 구독하고 반응할 수 있는 기능입니다. 이벤트는 특정 상황이나 조건에 따라 호출되는 메서드를 참조하고, 다른 클래스에서 이를 구독하거나 구독 해지하여 이벤트가 발생했을 때 일정한 동작을 수행할 수 있습니다. 


null 조건부 연산자 (?.)

델리게이트나 이벤트를 다루다보면 null인지 체크를 해줘야 합니다. 

만약 null인 델리게이트를 호출한다면 NullReferenceException이 발생하게 됩니다.

 

?. 연산자 ? 왼쪽의 항이 null이 아니라면 . 뒷부분을 실행하겠다는 의미의 연산자입니다.

OnHealthChanged?.Invoke(health);

 

함수의 경우 Invoke를 붙여서 호출할 수 있게 됩니다. 그리고 이 연산자의 경우 원자적으로 수행이 되는 연산자라서 이 연산 도중 다른 스레드가 개입할 여지가 없어 멀티 스레드 환경에서도 안전하게 돌아가게 됩니다.

 

"원자적으로 수행된다" 는 말은 어떤 작업이 더 이상 쪼개 수 없는 단일한 작업으로 수행된다는 의미입니다. 즉, 그 작업이 시작되면 도중에 끊기거나 중단되지 않고 완전히 끝날 때까지 다른 작업이 끼어들 수 없는 상태를 말합니다.

728x90