13. foreach의 열거자 패턴, struct 열거자 박싱, 버전 토큰

난이도 중 해설 보기 →

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

결함 코드 · C#
// 주제: foreach 의 컴파일러 패턴과 열거자(enumerator) — struct 열거자의 무할당, 박싱,
//       그리고 열거 중 컬렉션 변경 시의 동작(버전 토큰).
//
// 다음을 예측·설명하라:
//  (1) foreach (var x in list) 는 어떤 멤버 호출들로 풀리나? (GetEnumerator/MoveNext/Current/Dispose)
//      그리고 foreach 가 IEnumerable 인터페이스가 "꼭" 필요한가?
//  (2) List<int> 를 직접 foreach 할 때와, 같은 리스트를 IEnumerable<int> 로 받아 foreach 할 때
//      힙 할당이 어떻게 다른가? (struct 열거자 vs 박싱)
//  (3) (A): foreach 도중 컬렉션을 수정하면 무슨 일이 일어나나? 내부적으로 어떻게 감지하나?
//  (4) (B): struct 열거자를 변수에 복사해 MoveNext 하면 원본은 어떻게 되나?

using System.Collections;
using System.Collections.Generic;

public static class Demo
{
    // (1)(2) 구체 타입 vs 인터페이스
    public static int SumConcrete(List<int> xs)
    {
        int s = 0;
        foreach (var x in xs) s += x;   // List<int>.Enumerator (struct) 사용
        return s;
    }

    public static int SumInterface(IEnumerable<int> xs)
    {
        int s = 0;
        foreach (var x in xs) s += x;   // IEnumerator<int> (박싱된 열거자)
        return s;
    }

    // (3) 열거 중 수정
    public static void MutateWhileEnumerating(List<int> xs)
    {
        foreach (var x in xs)
        {
            if (x == 2) xs.Add(999);    // (A) 열거 도중 컬렉션 변경
        }
    }

    // (4) struct 열거자 복사
    public static void CopyEnumerator(List<int> xs)
    {
        List<int>.Enumerator e1 = xs.GetEnumerator();
        List<int>.Enumerator e2 = e1;   // (B) struct 복사
        e2.MoveNext();                  // e2 만 전진? e1 은?
    }
}
내 리뷰 · C#
내 답안 · 자동 저장

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