본문 바로가기
카테고리 없음

소멸자(~EmguVision)로 GDI 리소스(Pen)를 해제하는 방식은 권장되지 않습니다.

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

좋은 포인트예요. 결론부터:

  • 소멸자(~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 패턴 등으로 누수 방지코드 간결화

이렇게 정리하면, 반복되는 소멸자 코드 없이도 깨끗하고 안정적인 리소스 해제가 됩니다.

반응형