Delegate types in C#: Action vs Func vs Predicate

Published on in C#

Last updated on

Action doesn't return anything, Func does. Predicate takes one parameter and returns a boolean.

Table of contents

Action<T1, T2, T3, ...>

  • Action has no return value.
  • The types T1, T2, T3 and so on specify the parameters' types.
  • There can be between 0 and 16 parameters:
    • Action
    • Action<T1>
    • Action<T1, T2>
    • Action<T1, T2, T3>
    • ...
    • Action<T1, T2, T3, ..., T16>

Examples:

  • Action takes no parameters and returns nothing.
  • Action<int> takes one int parameter and returns nothing.
  • Action<int, bool, int> takes three parameters of types int, bool and int, in that order, and returns nothing.
Action lambda1 = () =>
{
Console.WriteLine("lambda1 was called");

// No return value
};

Action<int> lambda2 = (int param1) =>
{
Console.WriteLine($"lambda2 was called with {param1}");

// No return value
};

Action<int, bool, int> lambda3 = (int param1, bool param2, int param3) =>
{
Console.WriteLine($"lambda3 was called with {param1}, {param2} and {param3}");

// No return value
};

Func<T1, T2, T3, ..., TResult>

  • Func returns a value of type TResult.
  • The types T1, T2, T3 and so on specify the parameters' types.
  • There can be between 0 and 16 parameters:
    • Func<TResult>
    • Func<T1, TResult>
    • Func<T1, T2, TResult>
    • Func<T1, T2, T3, TResult>
    • ...
    • Func<T1, T2, T3, ..., T16, TResult>
  • TResult is always in the last position, i.e. the last type always specifies the return value's type.

Examples:

  • Func<string> takes no parameters and returns a string.
  • Func<int, string> takes one int parameter and returns a string.
  • Func<int, bool, int, string> takes three parameters of types int, bool and int, in that order, and returns a string.
Func<string> lambda1 = () =>
{
return "lambda1 was called";
};

Func<int, string> lambda2 = (int param1) =>
{
return $"lambda2 was called with {param1}";
};

Func<int, bool, int, string> lambda3 = (int param1, bool param2, int param3) =>
{
// `null` return value is ok too
// because strings can be null
if (!param2) return null;

return $"lambda3 was called with {param1}, {param2} and {param3}";
};

Predicate<T>

  • Predicate returns a boolean.
  • Predicate takes one parameter of type T.

Examples:

  • Predicate<int> takes one int parameter and returns a boolean.
  • Predicate<string> takes one string parameter and returns a boolean.
Predicate<int> IsPositive = (int param) =>
{
return param > 0;
};

Predicate<string> StartsWithFoo = (string param) =>
{
return param?.StartsWith("foo") ?? false;
};

Predicate<T> vs Func<T, bool>

Predicate<T> is effectively the same as Func<T, bool>. They both take a single parameter of type T and return a boolean.

That said, they are not assignment-compatible. Example from the linked Stack Overflow answer by Daniel Earwicker (slightly modified):

public static bool IsNegative(int x)
{
return x < 0;
}

static void Main(string[] args)
{
Predicate<int> p = IsNegative;
Func<int, bool> f = IsNegative;

p = f; // Doesn't work
f = p; // Doesn't work
}

Which one to use, Predicate<T> or Func<T, bool>?

If you can choose, my hunch is that Func is better because:

  • Func has overloads that take more than one input type, so it's more flexible than Predicate.
  • Func<T, bool> is clear (given that you know how Funcs work), whereas Predicate is more niche and makes sense only if you know what a "predicate" is.

On the other hand, Predicate<T> is short and descriptive, so I don't have a strong opinion which one to prefer.

Further resources

This blog post was inspired by Scott Steffes's YouTube video Generic Delegate Types (Action, Func, Predicate), in C#. There's interesting discussion about the delegate keyword versus lambda syntax in a Reddit thread around the video.

See also these pages in the .NET docs: