Question
In C#, what is the difference between Func<T, ...>, Action<T>, and Predicate<T> delegates, and when should each one be used?
Please explain them with practical, real examples so it is clear:
- When should I use a
Func<T, ...>delegate? - When should I use an
Action<T>delegate? - When should I use a
Predicate<T>delegate?
Short Answer
By the end of this page, you will understand that Func, Action, and Predicate are all delegate types in C# used to pass behavior as data. You will learn the key difference between them:
Funcreturns a valueActionperforms work and returns nothingPredicatereturnstrueorfalse
You will also see where they appear in real C# code, especially with collections, LINQ, validation, filtering, and callbacks.
Concept
In C#, a delegate is a type that can store a reference to a method. This lets you pass methods around like variables.
Func, Action, and Predicate are built-in generic delegate types that save you from creating custom delegates for common cases.
Action
Use Action when you want to do something but do not need a return value.
Examples:
- Print a message
- Log an error
- Update an object
- Send a notification
Action<string> print = message => Console.WriteLine(message);
print("Hello");
This means:
- input:
string - output: nothing (
void)
Func
Use Func when you want to calculate or transform something and return a result.
Examples:
- Convert text to uppercase
- Calculate tax
- Map one object into another
- Parse input into another type
Func<int, int, int> add = (a, b) => a + b;
Console.WriteLine(add(2, 3));
This means:
- inputs:
int,int - output:
int
The last type parameter is always the return type.
Predicate
Use Predicate when you want to test a condition and return only true or false.
Examples:
- Is a number even?
- Is a user active?
- Does a filename match a rule?
- Should this item be included?
Predicate<int> isEven = number => number % 2 == 0;
Console.WriteLine(isEven(4));
This means:
- input:
int - output:
bool
Why this matters
These delegates are used everywhere in modern C#:
- collection methods like
List<T>.ForEach,Find, andRemoveAll - LINQ methods like
Select,Where, andAny - callbacks and event-like behavior
- reusable validation and filtering logic
Understanding them helps you read and write cleaner, more flexible code.
Mental Model
Think of these three delegate types as three kinds of workers:
Action: a worker who does a task and does not hand anything backFunc: a worker who takes input, processes it, and returns a resultPredicate: a worker who checks a rule and answers only yes or no
Another simple way to remember them:
Action= DoFunc= ComputePredicate= Check
If your method needs to answer a question with true or false, Predicate is a natural fit.
If your method produces any other kind of result, use Func.
If your method just performs work, use Action.
Syntax and Examples
Core syntax
Action<T>
Func<T, TResult>
Func<T1, T2, TResult>
Predicate<T>
Action example
Action<string> greet = name => Console.WriteLine($"Hello, {name}!");
greet("Sam");
Use this when you want to perform an operation and return nothing.
Func example
Func<string, int> getLength = text => text.Length;
Console.WriteLine(getLength("apple"));
This takes a string and returns an int.
Predicate example
Predicate<string> isLongWord = word => word.Length > 5;
Console.WriteLine(isLongWord("banana"));
This checks whether a string matches a condition.
Real collection examples
Action with ForEach
var numbers = new List<int> { 1, 2, 3 };
numbers.ForEach(n => Console.WriteLine(n));
ForEach expects an Action<int> because it performs work for each item.
Predicate with FindAll
var numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
var evenNumbers = numbers.FindAll(n => n % 2 == 0);
Console.WriteLine(string.Join(", ", evenNumbers));
FindAll uses a condition, so it takes a Predicate<int>.
Func with Select
var numbers = new List<int> { 1, 2, 3 };
var squares = numbers.Select(n => n * n);
Console.WriteLine(string.Join(", ", squares));
Select transforms each item into a new value, so it uses a Func<int, int>.
Step by Step Execution
Consider this example:
var names = new List<string> { "Ana", "David", "Li" };
Predicate<string> isShort = name => name.Length <= 3;
Func<string, string> makeUpper = name => name.ToUpper();
Action<string> print = text => Console.WriteLine(text);
var shortNames = names.FindAll(isShort);
var upperNames = shortNames.Select(makeUpper);
foreach (var name in upperNames)
{
print(name);
}
Step by step
- A list of names is created:
["Ana", "David", "Li"]
-
isShortis aPredicate<string>.- It checks whether a name has length
<= 3. - For each input, it returns
trueorfalse.
- It checks whether a name has length
-
makeUpperis aFunc<string, string>.- It takes a string.
- It returns a new uppercase string.
-
printis anAction<string>.- It takes a string.
- It prints it.
- It returns nothing.
-
names.FindAll(isShort)tests each name:"Ana"->true"David"->false"Li"->true
Result:
["Ana", "Li"]
-
shortNames.Select(makeUpper)transforms each remaining name:"Ana"->"ANA""Li"->"LI"
-
The
foreachloop sends each result toprint:- prints
ANA - prints
LI
- prints
This example shows the three roles clearly:
PredicatefiltersFunctransformsActionperforms a side effect
Real World Use Cases
Action use cases
Use Action when code needs a callback that performs work:
- logging messages
- sending emails or notifications
- updating UI elements
- processing each item in a collection
- running custom code after a task finishes
void ProcessOrders(List<Order> orders, Action<Order> handleOrder)
{
foreach (var order in orders)
{
handleOrder(order);
}
}
Func use cases
Use Func when code needs a reusable calculation or transformation:
- formatting values
- converting DTOs to models
- computing totals
- generating keys or IDs
- selecting data in LINQ
Func<decimal, decimal> addTax = price => price * 1.2m;
Predicate use cases
Use Predicate when code needs a rule or test:
- validating ages, emails, or usernames
- filtering lists
- checking permissions
- deciding whether to keep or remove items
- finding matching records
Predicate<User> isActive = user => user.IsActive;
Real Codebase Usage
In real projects, these delegates often appear as small reusable pieces of business logic.
Validation and guard clauses
A Predicate<T> can hold a validation rule.
Predicate<string> isBlank = text => string.IsNullOrWhiteSpace(text);
if (isBlank(name))
{
throw new ArgumentException("Name is required.");
}
Transformation pipelines
A Func<T, TResult> is commonly used for mapping data.
Func<User, UserDto> toDto = user => new UserDto
{
Id = user.Id,
Name = user.Name
};
This is common in APIs and service layers.
Callbacks and side effects
An Action<T> is often passed into a method to customize behavior.
void ExecuteWithLogging(Action action)
{
Console.WriteLine("Starting...");
action();
Console.WriteLine("Finished.");
}
Filtering collections
While LINQ often uses Func<T, bool>, many collection APIs use Predicate<T> directly.
var users = new List<User>();
var activeUsers = users.FindAll(user => user.IsActive);
users.RemoveAll(user => !user.IsActive);
Early return patterns
A Predicate<T> can make intent clearer when deciding whether to continue.
Predicate<Order> isInvalid = order => order.Total <= 0;
if (isInvalid(order))
{
return;
}
Configurable behavior
Developers often accept delegates as parameters so methods can be reused with different behavior instead of hardcoding one approach.
Common Mistakes
1. Using Action when a return value is needed
Broken example:
Action<int, int> add = (a, b) => a + b;
This fails because Action must return void.
Correct:
Func<int, int, int> add = (a, b) => a + b;
2. Using Predicate<T> for non-boolean results
Broken example:
Predicate<int> doubleNumber = n => n * 2;
A Predicate<T> must return bool.
Correct:
Func<int, int> doubleNumber = n => n * 2;
3. Forgetting that Func's last type is the return type
Func<int, string, bool>
This means:
- input:
int - input:
string - return:
bool
Not three inputs.
4. Confusing Predicate<T> with Func<T, bool>
These are very similar.
Predicate<int> p = x => x > 0;
Func<int, bool> f = x => x > 0;
Both represent a method that takes an int and returns bool, but some APIs specifically ask for one or the other.
5. Ignoring readability
This works:
Func<User, bool> f = u => u.IsActive && u.Age >= 18 && u.EmailConfirmed;
But a descriptive name is often clearer:
Func<User, bool> isEligibleUser = u => u.IsActive && u.Age >= 18 && u.EmailConfirmed;
6. Overusing delegates for very simple code
Sometimes an inline lambda is enough. Do not create named delegates unless reuse or readability improves.
Comparisons
| Delegate | Purpose | Returns | Common Use |
|---|---|---|---|
Action<T> | Perform work | void | Logging, printing, updating, callbacks |
Func<T, TResult> | Compute or transform | Any type | Mapping, calculations, LINQ Select |
Predicate<T> | Test a condition | bool | Filtering, validation, matching |
Predicate<T> vs Func<T, bool>
| Option | Meaning | When used |
|---|---|---|
Predicate<T> | Takes T, returns bool | Common in List<T> methods like Find, Exists, RemoveAll |
Func<T, bool> | Takes T, returns bool | Common in LINQ methods like Where, Any, All |
Action vs Func
| Question | Use |
|---|---|
| Does this code return a value? | Func |
| Does this code only perform work? | Action |
Rule of thumb
- Use
Actionfor side effects - Use
Funcfor returned results - Use
Predicatefor yes/no tests
Cheat Sheet
// Action: input(s), no return value
Action<string> log = message => Console.WriteLine(message);
Action<int, int> printSum = (a, b) => Console.WriteLine(a + b);
// Func: input(s), returns a value
Func<int, int> square = x => x * x;
Func<int, int, int> add = (a, b) => a + b;
Func<string, bool> isEmpty = text => string.IsNullOrEmpty(text);
// Predicate: one input, returns bool
Predicate<int> isPositive = x => x > 0;
Quick rules
ActionreturnsvoidFuncreturns a valuePredicatereturnsbool- In
Func<...>, the last type is the return type Predicate<T>is similar toFunc<T, bool>
Common API usage
List<T>.ForEach->Action<T>List<T>.Find,FindAll,RemoveAll->Predicate<T>Enumerable.Select->Func<T, TResult>Enumerable.Where->Func<T, bool>
Memory aid
Action= doFunc= calculatePredicate= check
FAQ
What is the difference between Func and Action in C#?
Func returns a value. Action does not return anything and is used for work with side effects.
When should I use Predicate<T> instead of Func<T, bool>?
Use Predicate<T> when an API expects it, especially many List<T> methods. Use Func<T, bool> more often with LINQ.
Can a Predicate<T> have multiple input parameters?
No. Predicate<T> always takes one parameter of type T and returns bool.
Is Predicate<T> just a special case of Func<T, bool>?
Conceptually, yes. Both represent a method that takes one value and returns bool. They exist as separate delegate types because different APIs use different conventions.
Can I replace custom delegates with Func, Action, or Predicate?
Often, yes. If your delegate only matches one of these common shapes, using the built-in delegates usually keeps code simpler.
Why does LINQ use Func<T, bool> instead of Predicate<T>?
LINQ is designed around the more general Func family because it supports many kinds of return values, not just bool.
Are lambdas the same as Func, Action, and Predicate?
Not exactly. A lambda is an anonymous function expression. It can be assigned to a Func, Action, Predicate, or another compatible delegate type.
Mini Project
Description
Build a small console example that processes a list of products using all three delegate types. This demonstrates a realistic pattern: filter items, transform data, and then perform an action with the result.
Goal
Create a C# program that filters in-stock products, converts their names to display text, and prints the results.
Requirements
- Create a
Productclass withName,Price, andInStockproperties. - Store several products in a
List<Product>. - Use a
Predicate<Product>to keep only products that are in stock. - Use a
Func<Product, string>to convert each product into a display string. - Use an
Action<string>to print each display string.
Keep learning
Related questions
AddTransient vs AddScoped vs AddSingleton in ASP.NET Core Dependency Injection
Learn the differences between AddTransient, AddScoped, and AddSingleton in ASP.NET Core DI with examples and practical usage.
Best Way to Repeat a Character in C#: Building Repeated Strings Efficiently
Learn the best way to repeat a character in C#, compare StringBuilder, string concatenation, and simpler built-in options.
C# Access Modifiers and static: public, private, protected, and Defaults
Learn how C# public, private, protected, and default access control visibility, and how static differs from instance members.