먼저 결론부터 보자.
Predicate<T> = T 하나를 받아서 bool을 반환하는 델리게이트 타입
C#에서 실제 정의는 이렇게 생겼다.
public delegate bool Predicate<in T>(T obj);
느낌상 이런 거랑 비슷하다.
// 내가 직접 만든 델리게이트
delegate bool NumberTest(int n);
// .NET에서 미리 만들어 둔 델리게이트
Predicate<int> test;
NumberTest : “int → bool” 델리게이트 타입 (사용자 정의)Predicate<int> : “int → bool” 델리게이트 타입 (표준 제공)즉 “T가 조건을 만족하는지 검사해서 true/false를 돌려주는 함수”를 표현하기 위한 표준 타입이라고 보면 된다.
시그니처만 놓고 보면 둘은 사실 같다.
Predicate<T> // T를 받아서 bool 반환
Func<T, bool> // T를 받아서 bool 반환
둘 다 “T를 입력으로 받고 bool을 반환하는 함수”를 표현한다.
다만,
Predicate<T> : 이름 자체가 “조건 검사 함수”라는 의미를 담고 있음Func<T,bool> : 그냥 “T 받아서 bool 돌려주는 일반 함수”라는 의미
요즘 C# 코드에서는 Func<T,bool>를 많이 쓰지만,
List<T>, Array 같은 BCL API들은 예전부터 Predicate<T>를 사용해와서
둘 다 알아두는 게 좋다.
List<T>의 메서드들을 보면 Predicate<T>가 잔뜩 등장한다.
T Find(Predicate<T> match)List<T> FindAll(Predicate<T> match)int FindIndex(Predicate<T> match)bool Exists(Predicate<T> match)int RemoveAll(Predicate<T> match)bool TrueForAll(Predicate<T> match)숫자 리스트로 간단한 예제를 보자.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 3, 5, 4, 2, 6, 7, 8 };
// 짝수인지 판단하는 Predicate<int>
Predicate<int> isEven = n => n % 2 == 0;
// 1) 첫 번째 짝수 찾기
int firstEven = numbers.Find(isEven); // 4
// 2) 짝수 전부 찾기
List<int> evenList = numbers.FindAll(isEven); // { 4, 2, 6, 8 }
// 3) 짝수 요소를 전부 제거하기
int removedCount = numbers.RemoveAll(isEven);
// 4) 짝수가 하나라도 있나?
bool anyEven = numbers.Exists(isEven);
Console.WriteLine("첫 짝수: " + firstEven);
Console.WriteLine("제거된 짝수 개수: " + removedCount);
Console.WriteLine("짝수가 하나라도 남아 있는가? " + anyEven);
}
}
여기서 isEven의 타입이 바로 Predicate<int>다.
그리고 Find, FindAll, RemoveAll, Exists 메서드들은
내부에서 이 Predicate<int>를 호출하면서 조건을 검사한다.
실무에서는 Predicate<T> 변수를 따로 선언하기보다는,
람다식을 바로 넘겨버리는 패턴을 훨씬 많이 쓴다.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List<int> numbers = new List<int> { 3, 5, 4, 2, 6, 7, 8, 11, 13, 15, 20 };
// 10보다 큰 첫 번째 숫자 찾기
int biggerThanTen = numbers.Find(n => n > 10);
// 짝수만 모두 찾기
List<int> evens = numbers.FindAll(n => n % 2 == 0);
// 5보다 작은 수를 전부 지우기
int removed = numbers.RemoveAll(n => n < 5);
// 모든 숫자가 짝수인가?
bool allEven = numbers.TrueForAll(n => n % 2 == 0);
Console.WriteLine("10보다 큰 첫 숫자: " + biggerThanTen);
Console.WriteLine("짝수 리스트: " + string.Join(", ", evens));
Console.WriteLine("5보다 작은 수 제거 개수: " + removed);
Console.WriteLine("모든 수가 짝수인가? " + allEven);
}
}
여기서 n => n > 10 같은 람다식의 실제 타입은 Predicate<int>다.
즉 람다식이 “조건 함수” 역할을 하면서 List<T> 메서드 안으로 전달되고 있는 것이다.
class Product
{
public string ItemCode { get; set; }
public string CustomerCode { get; set; }
public decimal Price { get; set; }
public bool IsDeleted { get; set; }
}
class Program
{
static void Main()
{
List<Product> products = new List<Product>
{
new Product { ItemCode = "A-100", CustomerCode = "C001", Price = 1000, IsDeleted = false },
new Product { ItemCode = "A-200", CustomerCode = "C001", Price = 5000, IsDeleted = true },
new Product { ItemCode = "A-300", CustomerCode = "C002", Price = 7000, IsDeleted = false },
};
// 1) "C001" 거래처의 유효(삭제 안 된) 품목만 찾기
List<Product> validForC001 = products.FindAll(p =>
p.CustomerCode == "C001" &&
p.IsDeleted == false);
// 2) "삭제된 품목"만 전부 제거하기
int removedDeleted = products.RemoveAll(p => p.IsDeleted);
// 3) 모든 품목이 0보다 큰 가격인가?
bool allPricePositive = products.TrueForAll(p => p.Price > 0);
Console.WriteLine("C001 유효 품목 수: " + validForC001.Count);
Console.WriteLine("삭제된 품목 제거 수: " + removedDeleted);
Console.WriteLine("모든 품목 가격이 양수인가? " + allPricePositive);
}
}
여기서 p => p.CustomerCode == "C001" && !p.IsDeleted 등의 람다식 타입은 모두 Predicate<Product>다.
람다를 바로 넘기는 것도 좋지만, 일단 감 잡기용으로 타입을 직접 써보면 더 이해가 잘 된다.
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
// Predicate<int>를 명시적으로 선언
Predicate<int> isEven = n => n % 2 == 0;
// 직접 호출도 가능
bool r1 = isEven(4); // true
bool r2 = isEven(5); // false
Console.WriteLine(r1);
Console.WriteLine(r2);
// List<int>.Find에 그대로 넘기기
List<int> list = new List<int> { 3, 4, 5, 6 };
int firstEven = list.Find(isEven); // 4
Console.WriteLine("첫 짝수: " + firstEven);
}
}
사실 이건 아래 코드와 완전히 같은 의미다.
Func<int, bool> isEven2 = n => n % 2 == 0;
bool r3 = isEven2(4);
즉, Predicate<T>는 “조건 함수”라는 의미가 더 분명한 이름을 가진 Func<T,bool>라고 보면 된다.
T → boolList<T>.Find, FindAll, RemoveAll, Exists, TrueForAll 등T → boolWhere, Any, All 등T → void한 줄로 정리하면:
Predicate<T>는 “조건 검사용 델리게이트”이고, 형태만 놓고 보면 Func<T,bool>과 동일하다.