반응형
좋은 포인트예요. 결론부터:
- 소멸자(~EmguVision)로 GDI 리소스(Pen)를 해제하는 방식은 권장되지 않습니다.
WinForms 컨트롤은 Dispose(bool disposing) 를 override 해서 관리되는 리소스는 disposing == true일 때 해제하는 표준 패턴을 따르는 게 정석이에요.
소멸자(파이널라이저)는 성능에 불리하고, 호출 시점도 불확실하며, 관리 객체에 접근하면 위험합니다. → 소멸자 제거 권장. - InitializeComponent() 는 디자이너가 자동 생성하는 컨트롤 초기화(자식 컨트롤 배치, 속성 바인딩, 리소스 연결, 이벤트 wiring 등) 코드가 들어가는 자리입니다. 현재 내용이 비어있어도 “디자이너 초기화 진입점”으로 남겨두는 게 맞습니다.
아래에 권장 리팩토링을 드립니다.
1) 소멸자 제거 + 표준 Dispose 패턴으로 Pen/이미지 해제
방법 A) 간단·직관: Dispose(bool)에서 일괄 해제 (헬퍼 메서드 사용)
public partial class EmguVision : ScrollableControl
{
// 예: 필드
private Pen m_PenRed, m_PenLime, m_PenKhaki, m_PenWhite, m_PenPurple,
m_PenYellow, m_PenCyan, m_PenGray, m_PenAzure, m_PenOrange,
m_PenBlue, m_PenHotPink;
private EmguImage m_SrcImage, m_CpyImage, m_DstImage;
// 소멸자 삭제(비권장). ~EmguVision() { ... } 제거!
protected override void Dispose(bool disposing)
{
if (disposing)
{
// 1) 디자이너 컴포넌트 해제
components?.Dispose();
// 2) 우리 쪽 GDI/네이티브 리소스 해제
DisposeAndNull(ref m_PenRed);
DisposeAndNull(ref m_PenLime);
DisposeAndNull(ref m_PenKhaki);
DisposeAndNull(ref m_PenWhite);
DisposeAndNull(ref m_PenPurple);
DisposeAndNull(ref m_PenYellow);
DisposeAndNull(ref m_PenCyan);
DisposeAndNull(ref m_PenGray);
DisposeAndNull(ref m_PenAzure);
DisposeAndNull(ref m_PenOrange);
DisposeAndNull(ref m_PenBlue);
DisposeAndNull(ref m_PenHotPink);
DisposeAndNull(ref m_SrcImage);
DisposeAndNull(ref m_CpyImage);
DisposeAndNull(ref m_DstImage);
}
base.Dispose(disposing);
}
private static void DisposeAndNull<T>(ref T obj) where T : class, IDisposable
{
try { obj?.Dispose(); } catch { /* 로그 필요 시 기록 */ }
obj = null;
}
}
- 장점: 읽기 쉽고 안전합니다.
- 포인트: Dispose(bool disposing) 내부에서는 공용 프로퍼티/메서드 접근을 피하고, 필드만 직접 Dispose 하세요(불필요한 예외 방지).
방법 B) 확장성 좋은 Own 패턴(권장)
리소스를 생성하는 시점에 자동 등록해서 나중에 한 번에 정리합니다.
public partial class EmguVision : ScrollableControl
{
private readonly List<IDisposable> _owned = new();
private T Own<T>(T d) where T : IDisposable
{
_owned.Add(d);
return d;
}
// Pens 생성 시:
private void CreatePens()
{
m_PenRed = Own(new Pen(Color.Red, 1));
m_PenLime = Own(new Pen(Color.Lime, 1));
m_PenKhaki = Own(new Pen(Color.Khaki, 1));
m_PenWhite = Own(new Pen(Color.White, 1));
m_PenPurple = Own(new Pen(Color.Purple, 1));
m_PenYellow = Own(new Pen(Color.Yellow, 1));
m_PenCyan = Own(new Pen(Color.Cyan, 1));
m_PenGray = Own(new Pen(Color.Gray, 1));
m_PenAzure = Own(new Pen(Color.Azure, 1));
m_PenOrange = Own(new Pen(Color.Orange, 1));
m_PenBlue = Own(new Pen(Color.Blue, 1));
m_PenHotPink = Own(new Pen(Color.HotPink, 1));
}
protected override void Dispose(bool disposing)
{
if (disposing)
{
components?.Dispose();
// 우리가 소유한 모든 리소스를 한 번에 정리
foreach (var d in _owned)
{
try { d.Dispose(); } catch { /* 로그 */ }
}
_owned.Clear();
// 필드 null
m_PenRed = m_PenLime = m_PenKhaki = m_PenWhite = m_PenPurple =
m_PenYellow = m_PenCyan = m_PenGray = m_PenAzure = m_PenOrange =
m_PenBlue = m_PenHotPink = null;
m_SrcImage = m_CpyImage = m_DstImage = null;
}
base.Dispose(disposing);
}
}
- 장점: 이후 브러시/폰트/비트맵 등 추가 리소스가 생겨도 Own(...)만 붙이면 누수 없이 관리됩니다.
참고: Pen은 IComponent가 아니라서 components 컨테이너에 자동 등록되지 않습니다. 그러니 직접 Dispose 해줘야 합니다.
2) 생성자·InitializeComponent() 역할
- InitializeComponent() 는 디자이너가 생성하는 UI 초기화 코드가 들어갑니다.
일반적으로:- 자식 컨트롤 생성 및 Controls.Add(...)
- 속성 설정(크기, 폰트, 텍스트, 도킹, 색상 등)
- 이벤트 핸들러 연결
- 리소스(아이콘/이미지) 바인딩
- SuspendLayout()/ResumeLayout() 로 레이아웃 최적화
현재 당신의 designer.cs 내용은 거의 비어 있는데, 디자이너를 쓰지 않았거나 아직 구성 요소가 없는 상태로 보입니다. 그래도 관례상 생성자에서 InitializeComponent()를 먼저 호출하는 것이 맞습니다. 이후 사용자 코드(스타일, 더블버퍼, 펜 생성 등)를 이어서 호출하면 됩니다:
public EmguVision()
{
InitializeComponent(); // 디자이너 생성 코드(자식 컨트롤/속성 초기화)
// 사용자 초기화
DoubleBuffered = true;
BackColor = SystemColors.ControlDarkDark;
// 필요한 경우 리소스 생성
CreatePens(); // 위 Own 패턴 사용 예
}
마무리 체크리스트
- 소멸자 제거 (WinForms 컨트롤엔 비권장)
- Dispose(bool) override 에서 관리 리소스만 해제
- components?.Dispose() 호출 후, 직접 생성한 GDI/네이티브 리소스(Pen/Bitmap/EmguImage 등)도 확실히 Dispose
- 디자이너 자동 코드와 사용자 초기화를 분리(생성자에서 InitializeComponent() 먼저)
- 리소스 생성 시 Own 패턴 등으로 누수 방지와 코드 간결화
이렇게 정리하면, 반복되는 소멸자 코드 없이도 깨끗하고 안정적인 리소스 해제가 됩니다.
반응형