Sealed, Delegate, Event, EventHandler

이정은·2025년 8월 4일

C#

목록 보기
4/8

sealed

→ 봉인 용도

  • 클래스를 상속하지 못하도록 봉인.
  • 메서드를 오버라이딩하지 못하도록 막을 수도 있음.
  • 클래스 앞 → 더 이상 상속 불가
  • 오버라이드된 메서드 앞 → 자식에서 override 불가

클래스 봉인

using UnityEngine;

public sealed class BaseClass : MonoBehaviour
{
    public void Method()
    {
        Debug.Log("일반 함수");
    }
}

// 클래스 봉인으로 상속 불가
public class DerivedClass : BaseClass
{

}

일반적인 함수

using UnityEngine;

public class BaseClass : MonoBehaviour
{
    public void Method()
    {
        Debug.Log("일반 함수");
    }
}

가상 함수

using UnityEngine;

public class BaseClass : MonoBehaviour
{
    public virtual void Method()
    {
        Debug.Log("가상 함수");
    }
}

public class DerivedClass : BaseClass
{
    public override void Method()
    {
        Debug.Log("오버라이드 함수");
    }
}

using UnityEngine;

public class ParentClass : MonoBehaviour
{
    public virtual void Method() // 가상 함수
    {
        Debug.Log("Method");
    }
}

public class StudySealed : ParentClass
{
    public sealed override void Method() // 오버라이드된 함수
    {
        base.Method(); // 부모 클래스의 함수 기능을 가져오는 방법
        
        Debug.Log("Override Method");
    }
}

public class ChildClass : StudyClass
{
    public override void Method()
    {
        
    }
}

추상 함수

using UnityEngine;

public abstract class BaseClass : MonoBehaviour
{
    public abstract void Method();
}

public class DerivedClass : BaseClass
{
    public override void Method()
    {
        Debug.Log("오버라이드 함수");
    }
}

using UnityEngine;

public abstract class ParentClass : MonoBehaviour
{
    public abstract void Method(); // 추상 함수
}

public class StudySealed : ParentClass
{
    public sealed override void Method() // 오버라이드된 함수
    {
        Debug.Log("Override Method");
    }
}

public class ChildClass : StudyClass
{
    public override void Method()
    {
        
    }
}

봉인 함수

using UnityEngine;

public class BaseClass : MonoBehaviour
{
    public virtual void Method()
    {
        Debug.Log("일반 함수");
    }
}

public class MiddleClass : BaseClass
{
    public sealed override void Method()
    {
        Debug.Log("봉인된 오버라이드 함수");
    }
}

public class DerivedClass : MiddleClass
{
    // sealed된 함수는 자식 클래스에서 override 불가
    public override void Method()
    {
        Debug.Log("오버라이드 함수");
    }
}







delegate

: 함수를 참조 → 대리자 역할

  • 함수를 가리킬 수 있는 변수
  • C/C++의 함수 포인터와 비슷하지만, 안전하고 형식이 맞는 함수만 연결 가능.
  • 즉, 함수를 값처럼 전달하거나 콜백으로 실행할 때 사용.
    (함수를 변수처럼 전달 가능)
  • 함수를 변수에 저장하고 있다가 필요한 시점에 실행
  • 함수의 타입을 선언 → 해당 타입으로 변수를 선언 → 변수에 함수를 저장
접근제한자 delegate 반환타입 이름(매개변수)
public delegate void MyDelegate();

델리게이트 할당과 실행

using UnityEngine;

public class StudyDelegate : MonoBehaviour
{
    public delegate void MyDelegate();
    public MyDelegate onDelegate;

    void Start()
    {
        // Delegate 할당의 예전 방식
        onDelegate= new MyDelegate(MethodA);
    
        // Delegate 할당의 표준 방식
        onDelegate= MethodA;
    
        // 직접 실행 방식
        onDelegate();
        
        // 직접 실행과 동일한 방식
        onDelegate.Invoke();
        
        if (myDelegate != null) // null 체크가 없을 경우의 예외처리 방식
        {
            myDelegate.Invoke();
        }
        
        // null 체크 방식 -> 가장 안전한 방식
        onDelegate?.Invoke();
    }

    private void MethodA()
    {
        Debug.Log("Method A 실행");
    }
}

델리게이트 체인

using UnityEngine;

public class StudyDelegate : MonoBehaviour
{
    public delegate void MyDelegate();
    public MyDelegate onDelegate;

    void Start()
    {
        onDelegate = OnMethodA;
        
        onDelegate += OnMethodB;
        onDelegate += OnMethodC;
        
        onDelegate -= OnMethodB;

        onDelegate?.Invoke();
    }

    private void OnMethodA()
    {
        Debug.Log("Method A 실행");
    }
    
    private void OnMethodB()
    {
        Debug.Log("Method B 실행");
    }
    
    private void OnMethodC()
    {
        Debug.Log("Method C 실행");
    }
}

매개변수가 있는 Delegate

using UnityEngine;

public class StudyDelegate : MonoBehaviour
{
    public delegate void MyDelegate(int a, int b);
    public MyDelegate onDelegate;

    void Start()
    {
        onDelegate = OnMethodA;
        
        onDelegate += OnMethodB;
        onDelegate += OnMethodC;
        
        onDelegate -= OnMethodB;

        onDelegate?.Invoke(10, 20);
    }

    private void OnMethodA(int a, int b)
    {
        Debug.Log("Method A 실행");
    }
    
    private void OnMethodB(int c, int d)
    {
        Debug.Log("Method B 실행");
    }
    
    private void OnMethodC(int e, int f)
    {
        Debug.Log("Method C 실행");
    }
}

델리게이트의 매개변수 초기화

using UnityEngine;

public class StudyDelegate : MonoBehaviour
{
    public delegate void MyDelegate(int n = 0);
    public MyDelegate myDelegate;

    void Start()
    {
        myDelegate += MethodA; // 사용 X
        myDelegate += MethodB;

        myDelegate?.Invoke(); // 사용 가능 O
        myDelegate?.Invoke(10);
    }

    private void MethodA()
    {
        Debug.Log("Method A");
    }

    private void MethodB(int b)
    {
        Debug.Log("Method B");
    }
}

외부 클래스에서 함수로 등록하는 경우

using UnityEngine;

public class ExternalClass : MonoBehaviour
{
    private StudyDelegate studyDelegate;

    void Awake()
    {
        studyDelegate.AddMethod(StopEvent1);
        studyDelegate.AddMethod(StopEvent2);
    }

    private void StopEvent1()
    {
        Debug.Log("Stop Event 1");
    }
    
    private void StopEvent2()
    {
        Debug.Log("Stop Event 2");
    }
}

외부 클래스에서 static 델리게이트에 접근하여 등록하는 경우

using UnityEngine;

public class ExternalClass : MonoBehaviour
{
    void Awake()
    {
        StudyDelegate.onKeyDown += StopEvent1;
        StudyDelegate.onKeyDown += StopEvent2;
    }

    private void StopEvent1()
    {
        Debug.Log("Stop Event 1");
    }
    
    private void StopEvent2()
    {
        Debug.Log("Stop Event 2");
    }
}

OnEnable과 OnDisable을 활용한 안전한 이벤트 등록 / 해제 방식

using UnityEngine;
using UnityEngine.UI;

public class StudyDelegate : MonoBehaviour
{
    public delegate void TimerStart();
    public TimerStart onTimerStart;
    
    public delegate void TimerEnd();
    public TimerEnd onTimerEnd;

    private float timer = 5f;
    private bool isTimer = true;

    void OnEnable()
    {
        onTimerStart += StartEvent;
        onTimerEnd += EndEvent;
    }

    void Start()
    {
        onTimerStart?.Invoke();
    }

    void OnDisable()
    {
        onTimerStart -= StartEvent;
        onTimerEnd -= EndEvent;
    }
    
    void Update()
    {
        if (!isTimer)
            return;
        
        timer -= Time.deltaTime;

        if (timer <= 0f)
        {
            isTimer = false;
            onTimerEnd?.Invoke();
        }
    }

    private void StartEvent()
    {
        Debug.Log("타이머 시작");
    }

    private void EndEvent()
    {
        Debug.Log("타이머 종료");
    }
}







Event


: 어떤 객체가 발생시키는 동작 또는 상황을 이벤트라 함. 그 동작이나 상황을 다른 클래스나 객체에 알리고자 하는 용도로 사용됨.
  • 이벤트 할당 연산자 =는 사용 불가, +=(구독), -=(구독 해지)를 사용.
  • 외부에서는 구독(+=)과 해제(-=)만 가능, 호출은 선언한 클래스 안에서만 가능
  • 이벤트는 클래스의 캡슐화를 더욱 강화하여 안정성 높임

delegate vs event 비교

특징delegateevent
외부 호출 가능 여부가능불가능 (내부에서만 Invoke)
외부에서 할 수 있는 것추가/제거/호출추가/제거만 가능
안전성낮음높음



일반적으로 사용했던 델리게이트 방식

using UnityEngine;

public class StudyEvent : MonoBehaviour
{
    public delegate void InputKeyHandler();
    public InputKeyHandler onInputKey;

    void Start()
    {
        onInputKey += InputKeyEvent;
    }

    private void InputKeyEvent()
    {
        Debug.Log("Key Event");
    }
}


외부 클래스에서 델리게이트 실행 가능

using UnityEngine;

public class ExternalClass : MonoBehaviour
{
    public StudyEvent studyEvent;

    void Awake()
    {
        studyEvent = FindFirstObjectByType<StudyEvent>();
    }

    void Start()
    {
        studyEvent.onInputKey += Event1;
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            studyEvent.onInputKey?.Invoke();
        }
    }

    private void Event1()
    {
        Debug.Log("Event 1");
    }
}

event 키워드를 적용한 델리게이트

using UnityEngine;

public class StudyEvent : MonoBehaviour
{
    public delegate void InputKeyHandler(string msg);
    public event InputKeyHandler onInputKey;

    void Start()
    {
        onInputKey += InputKeyEvent;
    }
    
    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            onInputKey?.Invoke("Hello Unity");
        }
    }

    private void InputKeyEvent(string msg)
    {
        Debug.Log(msg);
    }
}

event를 사용했기 때문에 외부 클래스에서 델리게이트 실행 ❌ / ‘=’ 할당 ❌

using UnityEngine;

public class ExternalClass : MonoBehaviour
{
    public StudyEvent studyEvent;

    void Awake()
    {
        studyEvent = FindFirstObjectByType<StudyEvent>();
    }

    void Start()
    {
        studyEvent.onInputKey += Event1;

        // studyEvent.onInputKey = Event1;
    }

    // void Update()
    // {
    //     if (Input.GetKeyDown(KeyCode.Space))
    //     {
    //         studyEvent.onInputKey?.Invoke();
    //     }
    // }

    private void Event1(string s)
    {
        Debug.Log("Event 1 : " + s);
    }
}







EventHandler

: EventArgs를 매개변수로 받는 Delegate

  • 이벤트가 발생했을 때 실행되는 메서드(콜백) : 이벤트가 발생할 때마다 delegate에 의해 참조된 eventhandler가 호출된다.
  • +=로 이벤트에 연결
using System;
using UnityEngine;

public class StudyEventHandler : MonoBehaviour
{
    public event EventHandler handler;

    void OnEnable()
    {
        handler += MethodA;
    }

    void OnDisable()
    {
        handler -= MethodA;
    }

    void Update()
    {
        if (Input.GetKeyDown(KeyCode.Space))
        {
            handler?.Invoke(this, EventArgs.Empty);
        }
    }

    private void MethodA(object o, EventArgs e)
    {
        Debug.Log("Method A");
    }
}

0개의 댓글