8. IDisposable·파이널라이저와 Dispose 패턴 (결정적 해제와 SuppressFinalize)

난이도 중 해설 보기 →

결함을 모두 찾고 원인·수정안·더 나은 설계를 제시하라. 마커 (A)(B) 는 주목 위치 힌트다.

결함 코드 · C#
// ============================================================================
// [내부동작 문제] C# - IDisposable·파이널라이저와 Dispose 패턴
// ----------------------------------------------------------------------------
// 주제:
//   결정적 자원 해제(IDisposable/using)와 비결정적 정리(파이널라이저/~Finalizer),
//   GC.SuppressFinalize 의 역할, 그리고 "Dispose 를 깜빡했을 때" 무슨 일이 생기는지를
//   예측하고 설명한다.
//
// Handle 은 표준 Dispose 패턴을 흉내 낸다: 파이널라이저와 Dispose 를 모두 가지고,
// Dispose 에서 SuppressFinalize 를 호출한다. 각 경로는 "dispose: X" / "finalizer: X" 를
// 출력한다.
//
// 과제:
//   1) 아래 Main 의 출력(줄 내용과 순서)을 예측하라. 특히 A(using 으로 정리)와
//      B(Dispose 없이 버려짐)에 대해 파이널라이저가 각각 실행되는지 답하라.
//   2) Dispose 에서 GC.SuppressFinalize(this) 를 부르는 이유를 설명하라(부르지 않으면?).
//      파이널라이저가 도는 스레드·시점이 왜 비결정적이며, 자원(소켓/파일핸들 등)을
//      파이널라이저에만 의존하면 무엇이 문제인지 설명하라.
//   3) 게임서버에서 소켓/네이티브 핸들을 다룰 때 이 패턴(혹은 SafeHandle)이 왜 중요한지 적어라.
//   ※ 먼저 예측을 적은 뒤 answer.md 와 대조할 것.
// ============================================================================

using System;

public sealed class Handle : IDisposable
{
    private bool _disposed;
    public string Name;
    public Handle(string name) { Name = name; }

    ~Handle()
    {
        // 파이널라이저: GC 가 수거할 때 파이널라이저 스레드에서 실행
        Console.WriteLine($"finalizer: {Name}");
        ReleaseUnmanaged();
    }

    public void Dispose()
    {
        if (_disposed) return;
        ReleaseManaged();
        ReleaseUnmanaged();
        _disposed = true;
        GC.SuppressFinalize(this);            // (핵심) 이미 정리했으니 파이널라이저 불필요
        Console.WriteLine($"dispose: {Name}");
    }

    private void ReleaseManaged()   { /* 관리 자원 정리 */ }
    private void ReleaseUnmanaged() { /* 비관리 자원(핸들 등) 해제 */ }
}

public static class Program
{
    public static void Main()
    {
        using (var a = new Handle("A")) {     // 블록 끝에서 Dispose 자동 호출
        }

        var b = new Handle("B");              // Dispose 를 호출하지 않고
        b = null;                             // 참조만 끊는다

        GC.Collect();                         // 수거 유도
        GC.WaitForPendingFinalizers();        // 파이널라이저 완료까지 대기
        Console.WriteLine("done");
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

작성 후 위 해설 보기에서 모범 해설과 대조하세요.