Question

Func vs Action vs Predicate in C#: Differences, Uses, and Examples

csharpdelegatesactionfunc

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:

  • Func returns a value
  • Action performs work and returns nothing
  • Predicate returns true or false

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, and RemoveAll
  • LINQ methods like Select, Where, and Any
  • 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 back
  • Func: a worker who takes input, processes it, and returns a result
  • Predicate: a worker who checks a rule and answers only yes or no

Another simple way to remember them:

  • Action = Do
  • Func = Compute
  • Predicate = 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.

Take Quiz

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

  1. A list of names is created:
["Ana", "David", "Li"]
  1. isShort is a Predicate<string>.

    • It checks whether a name has length <= 3.
    • For each input, it returns true or false.
  2. makeUpper is a Func<string, string>.

    • It takes a string.
    • It returns a new uppercase string.
  3. print is an Action<string>.

    • It takes a string.
    • It prints it.
    • It returns nothing.
  4. names.FindAll(isShort) tests each name:

    • "Ana" -> true
    • "David" -> false
    • "Li" -> true

    Result:

["Ana", "Li"]
  1. shortNames.Select(makeUpper) transforms each remaining name:

    • "Ana" -> "ANA"
    • "Li" -> "LI"
  2. The foreach loop sends each result to print:

    • prints ANA
    • prints LI

This example shows the three roles clearly:

  • Predicate filters
  • Func transforms
  • Action performs 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;
Take Quiz

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

DelegatePurposeReturnsCommon Use
Action<T>Perform workvoidLogging, printing, updating, callbacks
Func<T, TResult>Compute or transformAny typeMapping, calculations, LINQ Select
Predicate<T>Test a conditionboolFiltering, validation, matching

Predicate<T> vs Func<T, bool>

OptionMeaningWhen used
Predicate<T>Takes T, returns boolCommon in List<T> methods like Find, Exists, RemoveAll
Func<T, bool>Takes T, returns boolCommon in LINQ methods like Where, Any, All

Action vs Func

QuestionUse
Does this code return a value?Func
Does this code only perform work?Action

Rule of thumb

  • Use Action for side effects
  • Use Func for returned results
  • Use Predicate for yes/no tests
Take Quiz

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

  • Action returns void
  • Func returns a value
  • Predicate returns bool
  • In Func<...>, the last type is the return type
  • Predicate<T> is similar to Func<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 = do
  • Func = calculate
  • Predicate = 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.

Take Quiz

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 Product class with Name, Price, and InStock properties.
  • 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.
Take Quiz

Keep learning

Related questions

View more