본문 바로가기
C#

“직접 델리게이트 콜백” → “.NET 표준 이벤트 패턴(Event + EventArgs)”

by 공부봇 2025. 9. 19.
반응형

 

왜 바꿨나? (Delegate → Event + EventArgs)

1) .NET 표준 이벤트 패턴에 맞춤

  • .NET에서 이벤트는 관례적으로 event EventHandler<TEventArgs> 형태를 써요.
  • 이렇게 하면 구독/해제(+=, -=), 멀티캐스트(여러 핸들러 동시 호출), 외부에서 임의 호출 차단 같은 이벤트 전용 보호 장치가 자동으로 따라옵니다.
    • 반면, public OnImageGrabbedEventDelegate OnImageGrabbedCallback; 같은 일반 필드 델리게이트
      • 외부에서 통째로 갈아끼우기가 가능(기존 구독이 날아갈 위험)
      • 외부 코드가 임의로 호출도 가능(캡슐화 약함)
      • 멀티 구독/해제 동작이 명확하지 않음

2) 시그니처 표준화: object sender, TEventArgs e

  • 이벤트 핸들러는 통일된 모양이라 도구/프레임워크/라이브러리(WinForms/WPF/디자이너/리플렉션 등)에서 다루기 쉬워요.
  • 팀원이 봐도 “아, 이건 이벤트구나” 즉시 이해됩니다.

3) 확장성과 버전 안정성

  • 델리게이트 시그니처를 나중에 바꾸면 바이너리 호환이 깨짐(모든 구독자 코드가 컴파일 에러).
  • EventArgs 파생 타입에 **필드를 ‘추가’**하는 건 비교적 안전(기존 필드 유지, 새 필드는 선택적 사용).
    → 이벤트 페이로드를 확장해도 구독자 쪽 대개 그대로 동작합니다.

4) 예외 격리/안전한 호출 패턴

  • 이벤트는 내부에서 핸들러별 try/catch로 구독자 예외를 격리하기 쉬운 패턴으로 운용합니다.
  • 스레드-세이프하게 지역 변수로 핸들러 캡처 → null 체크 → 호출하는 전형적인 안전 호출 패턴을 쓰게 됩니다.

EventArgs를 상속하는 의미

  • 의미적/관례적 마커: “이 타입은 이벤트 데이터를 운반한다.”라는 의도를 명확히 합니다.
  • 표준 델리게이트와 궁합: EventHandler<TEventArgs> 제네릭 델리게이트는 where TEventArgs : EventArgs 제약을 가정(관례).
    → ImageGrabbedEventArgs : EventArgs로 만들어 표준 이벤트 인프라에 자연스럽게 들어갑니다.
  • Empty 패턴과 일관성: 별도 데이터가 없으면 EventArgs.Empty를 쓰듯, 데이터가 있으면 파생 EventArgs를 쓰는 일관된 모델.
  • 런타임 특수 기능이 붙는 건 아님: 상속 자체가 특별한 동작을 추가하진 않지만, 프레임워크/도구 친화성이 올라갑니다.

짧은 비교

이전 (필드 델리게이트)

public delegate void OnImageGrabbedEventDelegate(byte[] pBuffer, int nCamera);
public OnImageGrabbedEventDelegate OnImageGrabbedCallback;

// 외부에서 통째로 교체 가능, 임의 호출도 가능(권장 X)
OnImageGrabbedCallback = MyHandler; 
OnImageGrabbedCallback?.Invoke(buf, cam);

변경 후 (표준 이벤트)

public sealed class ImageGrabbedEventArgs : EventArgs
{
    public byte[] Buffer { get; private set; }
    public int Camera { get; private set; }
    public ImageGrabbedEventArgs(byte[] buffer, int camera)
    {
        if (buffer == null) throw new ArgumentNullException(nameof(buffer));
        Buffer = buffer;
        Camera = camera;
    }
}

public event EventHandler<ImageGrabbedEventArgs> ImageGrabbed;

// 안전한 발생 패턴
private void RaiseImageGrabbed(byte[] buffer, int camera)
{
    var handler = ImageGrabbed;       // 스레드-세이프 캡처
    if (handler == null) return;

    try
    {
        handler(this, new ImageGrabbedEventArgs(buffer, camera));
    }
    catch (Exception ex)
    {
        // 구독자 예외 격리
        Console.WriteLine("subscriber error: " + ex.Message);
    }
}

// 외부 구독
simulation.ImageGrabbed += (s, e) =>
{
    // e.Buffer, e.Camera 사용
};

정리 한 줄

  • EventArgs 상속 + event EventHandler<T> 패턴은 .NET에서 표준, 안전, 확장 가능한 이벤트 설계입니다.
  • 델리게이트 필드에서 이벤트 패턴으로 바꾸면 캡슐화/다중구독/버전안정성/도구호환성이 모두 좋아집니다.
반응형