ZetCode

C# Predicate

last modified October 5, 2026

This article explains how to use predicates in C#. A predicate expresses a condition as a value that can be passed to methods, which makes filtering code shorter and easier to read.

Predicate

A predicate, in its general meaning, is a statement about something that is either true or false. In programming, a predicate is a single-argument function that returns a boolean value.

C# Predicate

Predicates in C# are implemented with delegates. The Predicate<T> delegate represents a method that defines a set of criteria and determines whether the specified object meets those criteria. Its signature is bool Predicate<T>(T obj).

C# Predicate example

The following example creates a Predicate<T> from a named method.

Program.cs
List<int> data = [1, -2, 3, 0, 2, -1];

var isPositive = new Predicate<int>(IsPositive);
var filtered = data.FindAll(isPositive);

Console.WriteLine(string.Join(", ", filtered));

static bool IsPositive(int val)
{
    return val > 0;
}
var isPositive = new Predicate<int>(IsPositive);

A predicate delegate is created; it takes the IsPositive method as its parameter.

var filtered = data.FindAll(isPositive);

We pass the predicate to the FindAll method of the list, which retrieves all values for which the predicate returns true.

static bool IsPositive(int val)
{
    return val > 0;
}

The IsPositive method returns true for all values greater than zero.

$ dotnet run
1, 3, 2

C# Predicate with anonymous method

The following example passes an anonymous method to the delegate.

Program.cs
List<int> data = [1, -2, 3, 0, 2, -1];

Predicate<int> isPositive = delegate (int val) { return val > 0; };
var filtered = data.FindAll(isPositive);

Console.WriteLine(string.Join(", ", filtered));

The example uses the delegate keyword to define an anonymous method, so no separate named method is needed.

$ dotnet run
1, 3, 2

C# Predicate with lambda expression

A C# lambda expression simplifies the creation of predicates. Lambda expressions are created with the => lambda declaration operator.

Program.cs
List<string> words = ["falcon", "wood", "tree", "rock", "cloud", "rain"];

Predicate<string> hasFourChars = word => word.Length == 4;

var words2 = words.FindAll(hasFourChars);
Console.WriteLine(string.Join(", ", words2));

In the example, we find all words that have four letters.

$ dotnet run
wood, tree, rock, rain

C# Predicate with Exists

The Exists method of a list determines whether the list contains elements that match the conditions defined by the specified predicate.

Program.cs
List<string> words = ["sky", "", "club", "spy", "silk", "summer",
    "war", "cup", "cloud", "coin", "small", "terse", "",
    "snow", "snail", "see"];

Predicate<string> isEmpty = string.IsNullOrEmpty;

if (words.Exists(isEmpty))
{
    Console.WriteLine("There is an empty string");
}
else
{
    Console.WriteLine("There is no empty string");
}

We check whether the list contains any empty strings. Because string.IsNullOrEmpty has the same signature as the delegate, it can be assigned directly to a Predicate<string>.

$ dotnet run
There is an empty string

C# Predicate with RemoveAll

The RemoveAll method of a list removes all the elements that match the conditions defined by the specified predicate.

Program.cs
List<string> words = ["sky", "town", "club", "spy", "silk", "snail",
    "war", "cup", "cloud", "coin", "small", "terse"];

Predicate<string> hasThreeChars = word => word.Length == 3;

words.RemoveAll(hasThreeChars);

Console.WriteLine(string.Join(", ", words));

We have a list of words, and we remove all words that have three letters.

$ dotnet run
town, club, silk, snail, cloud, coin, small, terse

C# Predicate with multiple conditions

The next example uses a predicate with two conditions.

Program.cs
List<Country> countries =
[
    new ("Iran", 80_840_713),
    new ("Hungary", 9_845_000),
    new ("Poland", 38_485_000),
    new ("India", 1_342_512_000),
    new ("Latvia", 1_978_000),
    new ("Vietnam", 95_261_000),
    new ("Sweden", 9_967_000),
    new ("Iceland", 337_600),
    new ("Israel", 8_622_000)
];

Predicate<Country> startsWithI = c => c.Name.StartsWith('I');
Predicate<Country> isLarge = c => c.Population > 10_000_000;

Predicate<Country> combined = c => startsWithI(c) && isLarge(c);

var result = countries.FindAll(combined);
Console.WriteLine(string.Join(", ", result));

record Country(string Name, int Population);

We create a list of countries and find all countries whose name starts with 'I' and whose population is over ten million. The 10_000_000 literal uses digit separators to make the magnitude easier to read.

Predicate<Country> startsWithI = c => c.Name.StartsWith('I');
Predicate<Country> isLarge = c => c.Population > 10_000_000;

We define two predicates.

Predicate<Country> combined = c => startsWithI(c) && isLarge(c);

We combine the two predicates with the && operator.

var result = countries.FindAll(combined);

We apply the combined predicate to the FindAll method.

$ dotnet run
Country { Name = Iran, Population = 80840713 }, Country { Name = India, Population = 1342512000 }

C# negating predicates

We can create a delegate that negates an already defined delegate.

Program.cs
List<string> words = ["falcon", "wood", "tree", "rock", "cloud", "rain"];

Predicate<string> hasFourChars = word => word.Length == 4;

var words2 = words.FindAll(Negate(hasFourChars));

Console.WriteLine(string.Join(", ", words2));

static Predicate<T> Negate<T>(Predicate<T> predicate) => x => !predicate(x);

The example negates the hasFourChars delegate with a generic helper method, so the same logic works for a predicate of any type.

$ dotnet run
falcon, cloud

These are the words whose length is not four letters.

C# predicate with Func

The Func delegate is a generic delegate type that accepts 0 to 16 input parameters and returns a value. Func<T, bool> has the same signature as Predicate<T>, but the two are distinct delegate types and are not implicitly convertible to each other. Libraries such as LINQ use Func<T, bool>, while List<T>.FindAll and Array.FindAll expect Predicate<T>; a lambda expression satisfies either one.

Program.cs
List<Person> data =
[
    new ("John Doe", "gardener"),
    new ("Robert Brown", "programmer"),
    new ("Lucia Smith", "teacher"),
    new ("Thomas Neuwirth", "teacher")
];

ShowOutput(data, p => p.Occupation == "teacher");

void ShowOutput(List<Person> list, Func<Person, bool> predicate)
{
    foreach (var person in list.Where(predicate))
    {
        Console.WriteLine($"{person.Name}, {person.Occupation}");
    }
}

record Person(string Name, string Occupation);

The example creates a list of persons. The ShowOutput method takes a predicate as its second parameter and prints all persons who are teachers.

$ dotnet run
Lucia Smith, teacher
Thomas Neuwirth, teacher

C# Predicate with Array.FindAll

The Array.FindAll method retrieves all the elements that match the conditions defined by the specified predicate.

Program.cs
User[] users =
[
    new (1, "John", "London", new DateOnly(2001, 4, 1)),
    new (2, "Lenny", "New York", new DateOnly(1997, 12, 11)),
    new (3, "Andrew", "Boston", new DateOnly(1987, 2, 22)),
    new (4, "Peter", "Prague", new DateOnly(1936, 3, 24)),
    new (5, "Anna", "Bratislava", new DateOnly(1973, 11, 18)),
    new (6, "Albert", "Bratislava", new DateOnly(1940, 12, 11)),
    new (7, "Adam", "Trnava", new DateOnly(1983, 12, 1)),
    new (8, "Robert", "Bratislava", new DateOnly(1935, 5, 15)),
    new (9, "Robert", "Prague", new DateOnly(1998, 3, 14))
];

var threshold = 60;
Predicate<User> isOlderThan = u => GetAge(u) > threshold;

var result = Array.FindAll(users, isOlderThan);

foreach (var user in result)
{
    Console.WriteLine($"{user.Name} ({user.City}), born {user.DateOfBirth:yyyy-MM-dd}");
}

static int GetAge(User user)
{
    var today = DateOnly.FromDateTime(DateTime.Today);
    var age = today.Year - user.DateOfBirth.Year;

    if (user.DateOfBirth > today.AddYears(-age))
    {
        age--;
    }

    return age;
}

record User(int Id, string Name, string City, DateOnly DateOfBirth);

We get all users who are older than 60.

Predicate<User> isOlderThan = u => GetAge(u) > threshold;

In the predicate definition, we use the GetAge method to determine the age of the user.

var result = Array.FindAll(users, isOlderThan);

The Array.FindAll method retrieves all the elements that match the conditions defined by the specified predicate.

static int GetAge(User user)
{
    var today = DateOnly.FromDateTime(DateTime.Today);
    var age = today.Year - user.DateOfBirth.Year;

    if (user.DateOfBirth > today.AddYears(-age))
    {
        age--;
    }

    return age;
}

The GetAge method computes the exact age in years with DateOnly, subtracting one year when the birthday has not yet occurred. Unlike dividing days by 365.25, this approach stays correct across leap years.

$ dotnet run
Peter (Prague), born 1936-03-24
Albert (Bratislava), born 1940-12-11
Robert (Bratislava), born 1935-05-15

C# generic FindAll

Next, we define a FindAll list extension method that accepts several predicates at once.

ExtensionMethods.cs
public static class ExtensionMethods
{
    public static List<T> FindAll<T>(this List<T> vals, List<Predicate<T>> preds)
    {
        List<T> data = [];

        foreach (T e in vals)
        {
            if (preds.All(p => p(e)))
            {
                data.Add(e);
            }
        }

        return data;
    }
}

The FindAll method returns the list elements that satisfy all the specified predicates.

public static List<T> FindAll<T>(this List<T> vals, List<Predicate<T>> preds)

The FindAll method takes a list of generic predicate functions as a parameter and returns a filtered generic list. The instance method List<T>.FindAll takes a single predicate, so this overload is chosen for a list of predicates.

Program.cs
List<Predicate<int>> preds = [e => e > 0, e => e % 2 == 0];

List<int> vals = [-3, -2, -1, 0, 1, 2, 3, 4];
var filtered = vals.FindAll(preds);

foreach (var e in filtered)
{
    Console.WriteLine(e);
}

Console.WriteLine("---------------------");

List<string> words = ["sky", "wrath", "wet", "sun", "pick", "who",
    "cloud", "war", "water", "jump", "ocean"];

List<Predicate<string>> preds2 = [e => e.StartsWith('w'), e => e.Length == 3];

var filtered2 = words.FindAll(preds2);

foreach (var e in filtered2)
{
    Console.WriteLine(e);
}

We define two lists: an integer list and a string list. From the integer list, we keep all positive even values. From the string list, we get all words that start with 'w' and have three letters.

$ dotnet run
2
4
---------------------
wet
who
war

Source

Predicate<T> Delegate - .NET API

List<T>.FindAll Method - .NET API

Array.FindAll Method - .NET API

In this article we have worked with C# Predicate<T> delegates.

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all C# tutorials.