9. 델리게이트/이벤트: 멀티캐스트 예외, 구독 동일성, 구독 누수

난이도 상 해설 보기 →

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

결함 코드 · C#
// ============================================================================
// [내부동작 문제] C# - 델리게이트/이벤트: 멀티캐스트 호출·예외, 구독 동일성과 누수
// ----------------------------------------------------------------------------
// 주제:
//   멀티캐스트 델리게이트의 호출 순서와 "중간 핸들러가 예외를 던지면" 나머지 핸들러가
//   어떻게 되는지, 그리고 이벤트 구독(+=)/해지(-=)가 델리게이트 "동일성"에 어떻게 의존하는지
//   (메서드 그룹 vs 인라인 람다)와 그로 인한 구독 누수(메모리 릭)를 예측하고 설명한다.
//
// 과제:
//   1) Part A: 세 핸들러 중 두 번째가 예외를 던질 때, 출력되는 줄과 순서를 예측하라.
//      h3 는 실행되는가? 예외는 어디서 잡히는가?
//   2) Part B: 메서드 그룹 Named 와 인라인 람다를 구독한 뒤 각각 -= 로 해지를 시도한다.
//      두 번의 count 출력과 마지막 Fire() 의 출력을 예측하라. 인라인 람다 해지가
//      "되는지/안 되는지"와 그 이유(델리게이트 인스턴스 동일성)를 설명하라.
//   3) 장수(long-lived) 퍼블리셔가 단명(short-lived) 구독자의 메서드를 구독했을 때
//      구독을 해지하지 않으면 왜 메모리 누수가 되는지, 게임서버 관점에서 설명하라.
//   ※ 먼저 예측을 적은 뒤 answer.md 와 대조할 것.
// ============================================================================

using System;

public sealed class EventBus
{
    public event Action OnEvent;
    public void Fire() => OnEvent?.Invoke();
    public int HandlerCount => OnEvent?.GetInvocationList().Length ?? 0;
}

public static class Program
{
    static void Named() => Console.WriteLine("named");

    public static void Main()
    {
        // ---- Part A: 멀티캐스트 호출 중 예외 ----
        var a = new EventBus();
        a.OnEvent += () => Console.WriteLine("h1");
        a.OnEvent += () => { Console.WriteLine("h2"); throw new InvalidOperationException("boom"); };
        a.OnEvent += () => Console.WriteLine("h3");
        try { a.Fire(); }
        catch (Exception e) { Console.WriteLine($"caught: {e.Message}"); }

        Console.WriteLine("---");

        // ---- Part B: 구독/해지의 동일성 ----
        var b = new EventBus();
        b.OnEvent += Named;                                  // 메서드 그룹
        b.OnEvent += () => Console.WriteLine("inline");       // 인라인 람다 (참조 보관 안 함)
        Console.WriteLine($"count={b.HandlerCount}");

        b.OnEvent -= Named;                                  // 해지 시도 (1)
        b.OnEvent -= () => Console.WriteLine("inline");       // 해지 시도 (2): 새 람다
        Console.WriteLine($"count={b.HandlerCount}");
        b.Fire();
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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