1. List.Find란?
public T Find(Predicate<T> match);
- List<T> 클래스에 포함된 메서드
- 제네릭 리스트 안에서 특정 조건(Predicate)에 맞는 첫 번째 요소를 찾아서 반환
- 조건에 맞는 요소가 없으면 default(T)를 반환 (참조형이면 null, 값형이면 0 등)
사용 예제
List<int> numbers = new List<int> { 1, 3, 5, 7, 9 };
// 조건: 짝수인 첫 번째 원소 찾기
int found = numbers.Find(n => n % 2 == 0);
Console.WriteLine(found); // 출력: 0 (int의 기본값)
과정:
- 리스트의 첫 번째 요소(1) → 조건 함수 실행 (1 % 2 == 0) → false
- 두 번째 요소(3) 검사 → false
- …
- 끝까지 true가 없었으므로 default(int)인 0 반환
풀어서 쓴 형태
public static T MyFind<T>(List<T> list, Predicate<T> match)
{
// 리스트 전체를 앞에서부터 순서대로 검사
for (int i = 0; i < list.Count; i++)
{
// 조건(match)이 true라면 해당 요소 반환
if (match(list[i]))
{
return list[i];
}
}
// 조건을 만족하는 요소가 하나도 없으면 기본값 반환
return default(T);
}
2. Find vs FindAll vs ForEach
조건을 만족하는 요소를 찾거나 수정할 때, Find 말고도 FindAll, ForEach를 활용할 수 있습니다.
(1) Find – 첫 번째 요소만
var found = achievements.Find(a => a.currentCount == a.maxCount);
if (found != null)
{
found.isUnlocked = true;
}
- 조건을 만족하는 첫 번째 요소만 반환
- 여러 개 중 하나만 처리할 때 적합
(2) FindAll – 조건 맞는 모든 요소
var foundAll = achievements.FindAll(a => a.currentCount == a.maxCount);
foreach (var achievement in foundAll)
{
achievement.isUnlocked = true;
}
- 조건을 만족하는 모든 요소 반환
- 반환된 리스트를 돌면서 원하는 수정 가능합니다.
(3) ForEach – 리스트 전체 순회
achievements.ForEach(a =>
{
if (a.currentCount == a.maxCount)
{
a.isUnlocked = true;
}
});
- 리스트 전체를 순회하면서 조건 검사 및 수정
- 가장 간결하지만, 가독성은 foreach보다 떨어질 수 있습니다.
(4) FindAll + ForEach 조합
achievements.FindAll(a => a.currentCount == a.maxCount)
.ForEach(a =>
{
a.isUnlocked = true;
});
// 조건에 맞는 요소만 필터링 (foreach의 if와 같은 역할)
.FindAll(a => a.currentCount == a.maxCount)
// 조건 맞는 요소에 대해 실행할 동작 (if문 내부 코드와 같은 역할)
.ForEach(a => a.isUnlocked = true);
풀어서 쓰면:
foreach (var achievement in achievements)
{
if (achievement.currentCount == achievement.maxCount)
{
achievement.isUnlocked = true;
}
}
즉, foreach에서는 조건(if) + 실행(동작)을 한 블록 안에 같이 쓰는 반면,
FindAll + ForEach 조합에서는 조건(Filter)과 실행(Action)을 메서드 체인으로 분리해서 표현합니다.
그러면 의문이 들 수 있습니다.
foreach만 있으면 다 되는데, 왜 굳이 Find, FindAll, ForEach 같은 메서드를 따로 만들었을까?
이유 1. 간결성과 표현력
foreach는 가장 직관적이지만, 코드를 조금 길게 써야 합니다.
Find 같은 메서드를 쓰면 한 줄로 표현할 수 있습니다.
// foreach
string result = null;
foreach (var name in names)
{
if (name.StartsWith("C"))
{
result = name;
break;
}
}
// Find
string result = names.Find(n => n.StartsWith("C"));
같은 기능을 더 짧고 의도 중심으로 표현 가능
이유 2. 재사용성과 조합성
Find, FindAll은 조건을 만족하는 요소 집합을 반환하므로, 다른 메서드와 쉽게 조합할 수 있습니다.
// 조건 맞는 모든 요소만 골라낸 뒤
var completed = achievements.FindAll(a => a.currentCount == a.maxCount);
// 다시 다른 작업에 활용 가능
completed.ForEach(a => a.isUnlocked = true);
이유 3. 함수형 스타일 & 가독성 (상황에 따라)
LINQ, 람다와 함께 쓰면 데이터 → 조건 → 결과 흐름을 선언적(Declarative) 으로 쓸 수 있습니다.
achievements
.FindAll(a => a.currentCount == a.maxCount)
.ForEach(a => a.isUnlocked = true);
업적 중에 완료된 것만 골라 언락한다라는 의도가 코드에서 바로 보입니다.
'C# 문법' 카테고리의 다른 글
| [C#] Regex.Split (1) | 2025.08.26 |
|---|---|
| [C#] is, as 키워드 (형변환 문법) (0) | 2025.07.18 |
| [C#] StringBuilder (0) | 2025.04.16 |
| [C#] 델리게이트, 람다 및 LINQ (0) | 2025.04.08 |
| [C#] 예외처리(Try-catch), 값형과 참조형, 박싱과 언박싱 (0) | 2025.04.07 |