-
C# 이중 반복문(중첩 루프) 개선 방법닷넷/C# 2025. 6. 30. 13:23반응형
개발을 하다 보면 이중 반복문을 통해 문제를 해결하는 상황이 종종 발생한다.
foreach (var item in lists) { foreach (var relation in item.Relations) { relation.Status = Common.Enums.DataStatus.Unchanged; } }
위와 같은 이중 foreach 문이 기능상 문제가 있는 건 아니지만, 좀 더 깔끔하고 현대적인 C# 코드로 바꾸는 방법에 대해 알아보도록 하겠습니다.
1. SelectMany로 한 줄에 쭉 펼치기
LINQ의 SelectMany를 이용하면 중첩된 컬렉션을 평탄화(flatten)해서 단일 foreach로 처리할 수 있습니다.
foreach (var relation in lists.SelectMany(f => f.Relations)) { relation.Status = Common.Enums.DataStatus.Unchanged; }
- 장점: ‘모든 Relation’을 한 번에 처리한다는 의도가 명확해집니다.
- 단점: LINQ가 익숙하지 않은 팀원에게는 다소 낯설 수 있어요.
2. List<T>.ForEach 체이닝
만약 Functions와 Relations가 List<T> 타입이라면, ForEach 메서드를 중첩해서 사용할 수도 있습니다.
lists.ForEach(f => f.Relations.ForEach(r => r.Status = Common.Enums.DataStatus.Unchanged ) );
- 장점: 코드가 아주 간결해집니다.
- 단점: List<T> 전용이고, 중첩 람다가 가독성을 해칠 수 있어요.
3. 평탄화 후 리스트로 변환하고 ForEach 사용
SelectMany로 평탄화한 뒤 ToList()로 즉시 컬렉션을 만들고, List<T>.ForEach를 이어붙이는 방법입니다.
lists .SelectMany(f => f.Relations) .ToList() .ForEach(r => r.Status = Common.Enums.DataStatus.Unchanged);
- 장점: IEnumerable<T>에도 적용 가능하고, 코드가 플루언트하게 흘러갑니다.
- 단점: 중간에 임시 리스트가 하나 더 생성됩니다.
4. 범용 ForEach 확장 메서드 만들기
프로젝트 전반에서 IEnumerable<T>에 ForEach를 쓰고 싶다면, 확장 메서드를 정의해두세요.
public static class EnumerableExtensions { public static void ForEach<T>(this IEnumerable<T> source, Action<T> action) { foreach (var item in source) action(item); } }
이제 이렇게 쓸 수 있습니다.
lists .SelectMany(f => f.Relations) .ForEach(r => r.Status = Common.Enums.DataStatus.Unchanged);
- 장점: List<T>뿐 아니라 모든 IEnumerable<T>에 일관된 스타일 적용 가능!
- 단점: 초기 설정(확장 메서드 추가)이 필요합니다.
5. 병렬 처리로 속도 높이기
데이터 양이 매우 많아서 성능이 관건이라면 PLINQ를 활용해 병렬 처리도 고려해볼 수 있습니다.
lists .AsParallel() .SelectMany(f => f.Relations) .ForAll(r => r.Status = Common.Enums.DataStatus.Unchanged);
- 장점: 멀티코어 CPU를 활용해 처리 속도를 끌어올릴 수 있습니다.
- 단점: 동시성 이슈(스레드 세이프티)를 반드시 검토해야 하고, 소규모 데이터에는 오히려 오버헤드가 될 수 있어요.
- 같이 보면 좋을 내용: 작은 루프 본문 속도 개선
방법: 작은 루프 본문 속도 개선 - .NET
자세히 알아보기: 방법: 작은 루프 본체 속도 향상
learn.microsoft.com
마무리: 내 프로젝트에는 어떤 방법이 좋을까?
- 가독성과 간결함을 최우선으로 한다면 → 방법 1 (SelectMany + 단일 foreach)
- 람다 체이닝이 편하고 List<T>만 사용한다면 → 방법 2 (List.ForEach)
- 함수형 스타일을 전사적으로 도입하고 싶다면 → 방법 4 (확장 메서드)
- 대량 데이터 성능이 중요하다면 → 방법 5 (병렬 처리)
늘 그렇듯, <정답>은 없다.
팀의 코드 스타일, 프로젝트 특성, 성능 요구사항 등을 고려해 가장 적합한 방법을 선택하면 될 듯 하다.
반응형'닷넷 > C#' 카테고리의 다른 글
C# Dictionary와 LINQ로 구현하는 고객 유형별 상품 타입 필터링 패턴 (1) 2025.06.30 NullReferenceException, Null 참조 오류 등 Null 예외 처리에 대한 고찰과 지침서 (1) 2024.07.12 Microsoft의 Text-to-Speech (TTS), Azure Cognitive Services를 C#에서 사용하는 방법 (0) 2023.07.27 C# Interlocked.Increment에 대한 고찰 (0) 2023.07.20 C# 기초지식, 정보 정리 (면접 질문 대비, 꼭 알아야 하는 것) (0) 2023.04.11