반응형
왜 바꿨나? (Delegate → Event + EventArgs)
1) .NET 표준 이벤트 패턴에 맞춤
- .NET에서 이벤트는 관례적으로 event EventHandler<TEventArgs> 형태를 써요.
- 이렇게 하면 구독/해제(+=, -=), 멀티캐스트(여러 핸들러 동시 호출), 외부에서 임의 호출 차단 같은 이벤트 전용 보호 장치가 자동으로 따라옵니다.
- 반면, public OnImageGrabbedEventDelegate OnImageGrabbedCallback; 같은 일반 필드 델리게이트는
- 외부에서 통째로 갈아끼우기가 가능(기존 구독이 날아갈 위험)
- 외부 코드가 임의로 호출도 가능(캡슐화 약함)
- 멀티 구독/해제 동작이 명확하지 않음
- 반면, 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에서 표준, 안전, 확장 가능한 이벤트 설계입니다.
- 델리게이트 필드에서 이벤트 패턴으로 바꾸면 캡슐화/다중구독/버전안정성/도구호환성이 모두 좋아집니다.
반응형
'C#' 카테고리의 다른 글
“C# 값 형식에도 없음(null)을 — T? = Nullable<T> 제대로 쓰는 법” (0) | 2025.10.10 |
---|---|
error. 이벤트핸들러 이름 충돌 (0) | 2025.09.22 |
volatile는 멀티스레드에서 “가벼운 동기화”를 위한 읽기/쓰기 가시성 보장 장치 (0) | 2025.09.19 |
왜 IDisposable을 구현하나요? (0) | 2025.09.19 |
sealed를 왜 써야 할까? (0) | 2025.09.19 |
C#의 readonly 키워드 (0) | 2025.09.19 |
null-coalescing assignment 연산자(??=) 가 C# 8.0에서 추가된 문법이라, C# 7.3 컴파일러로는 사용할 수 없다는 뜻입니다. (0) | 2025.09.11 |
VisionThread/EmguVision 라이브·검사 공통 파이프라인 리팩토링 (풀코드) (0) | 2025.09.10 |