C#컬렉션과 LINQ

IEnumerable와 List, materialization

LINQ 결과를 바로 `ToList()` 해야 하는지, `IEnumerable<T>`로 늦게 평가해도 되는지를 어떤 기준으로 판단하면 좋은지 정리합니다.

마지막 수정 2026년 3월 22일

기본 패턴

csharp
IEnumerable<Player> query = players
    .Where(player => player.Score >= 1000)
    .OrderByDescending(player => player.Score);

List<Player> snapshot = query.ToList();

설명

  • IEnumerable<T>는 "지금 당장 다 만들어진 컬렉션"보다 "열거 가능한 흐름"에 가깝습니다. 그래서 LINQ 결과를 받으면 실제로는 지연 실행과 materialization 여부를 함께 생각해야 합니다.
  • ToList(), ToArray()를 호출하면 그 시점에 결과를 전부 계산해 메모리에 확정합니다. 이것이 materialization입니다. 반면 IEnumerable<T> 상태로 두면 나중에 열거할 때마다 다시 계산될 수 있습니다.
  • 중요한 판단 기준은 세 가지입니다. 결과를 여러 번 재사용할 것인가, 원본 컬렉션이 이후 바뀔 수 있는가, 계산 비용이 큰가. 이 셋 중 하나라도 강하게 작용하면 materialization이 오히려 더 명확할 수 있습니다.
  • 반대로 한 번만 순회하고 끝나는 흐름이라면 굳이 조기 ToList()를 넣지 않는 편이 메모리와 의도를 더 깔끔하게 유지합니다.
  • LINQ 성능 문제의 상당수는 쿼리 문법보다 "언제 확정하고 언제 미루는가"에서 나옵니다. 그래서 IEnumerable<T>List<T>의 차이는 타입 선택이 아니라 평가 전략 선택으로 보는 편이 좋습니다.

빠른 정리

선택의미
IEnumerable<T>지연 실행 가능, 열거 시 계산
List<T>결과를 지금 확정한 스냅샷
ToList()materialization
잘 맞는 경우재사용, 스냅샷 필요, 계산 비용 큼
피해야 할 습관이유 없는 조기 ToList()

주의할 점

IEnumerable<T>를 여러 번 순회하면 매번 계산이 다시 일어날 수 있습니다. 반대로 ToList()를 너무 빨리 넣으면 메모리 사용과 전체 계산 비용이 예상보다 커질 수 있으니, 평가 시점을 의식적으로 고르는 편이 중요합니다.

참고 링크

2 sources