.NETEasy
Func, Action, Predicate — when to use which delegate type in C#?
All three are built-in generic delegates. They differ only in what they return and what they accept.
| Delegate | Signature | Returns | Use for |
|---|---|---|---|
Action | Action<T1..T16> | void | side-effecting operations |
Func | Func<T1..T16, TResult> | TResult | mappers, factories |
Predicate<T> | Predicate<T> | bool | filter conditions |
Examples
Action<string> log = msg => Console.WriteLine(msg);
Func<int, int, int> add = (a, b) => a + b;
Predicate<Order> isPaid = o => o.Status == OrderStatus.Paid;
Why Predicate<T> exists if Func<T, bool> works
Historically Predicate<T> predates generics-heavy LINQ. Today prefer Func<T, bool> for interop — every LINQ method (Where, Any, All) takes Func<T, bool>, not Predicate<T>. List<T>.Find is one of the few APIs still on Predicate<T>.
Real-world rule of thumb
- Returns void →
Action - Returns bool for filtering →
Func<T, bool>(LINQ-compatible) - Returns anything else →
Func<..., TResult> - Defining a public API surface? Skip these and define a named delegate or interface — better stack traces, better tooling.
Closures gotcha
var handlers = new List<Action>();
for (int i = 0; i < 3; i++)
handlers.Add(() => Console.WriteLine(i)); // all print 3
Capture i into a local inside the loop to fix.