Delegates: Predicate vs. Action vs. Func
C#DelegatesPredicateFuncC# Problem Overview
Can someone provide a good explanation (hopefully with examples) of these 3 most important delegates:
- Predicate
- Action
- Func
C# Solutions
Solution 1 - C#
-
Predicate
: essentiallyFunc<T, bool>
; asks the question "does the specified argument satisfy the condition represented by the delegate?" Used in things like List.FindAll. -
Action
: Perform an action given the arguments. Very general purpose. Not used much in LINQ as it implies side-effects, basically. -
Func
: Used extensively in LINQ, usually to transform the argument, e.g. by projecting a complex structure to one property.
Other important delegates:
-
EventHandler
/EventHandler<T>
: Used all over WinForms -
Comparison<T>
: LikeIComparer<T>
but in delegate form.
Solution 2 - C#
Action
, Func
and Predicate
all belong to the delegate family.
Action
: Action can take n input parameters but it returns void.
Func
: Func can take n input parameters but it will always return the result of the provided type. Func<T1,T2,T3,TResult>
, here T1,T2,T3 are input parameters and TResult is the output of it.
Predicate
: Predicate is also a form of Func but it will always return bool. In simple words it is wrapper of Func<T,bool>
.
Solution 3 - C#
In addition to Jon's answer, there is also
Converter<TInput, TOutput>
: It's essentiallyFunc<TInput, TOutput>
, but with semantics. Used by List.ConvertAll and Array.ConvertAll, but personally haven't seen it anywhere else.
Solution 4 - C#
A simple example about the arguments and what retutn each type
This Func take two int arguments and return an int.Func always has return type
Func<int, int, int> sum = (a, b) => a + b;
Console.WriteLine(sum(3, 5));//Print 8
In this case func doesn't have arguments but return a string
Func<string> print = () => "Hello world";
Console.WriteLine(print());//Print Hello world
This Action take two int arguments and return void
Action<int, int> displayInput = (x, y) => Console.WriteLine("First number is :" + x + " , Second number is "+ y);
displayInput(4, 6); //Print First number is :4 , Second number is :6
This Predicate take one argument and always return bool.Generally Predicates always return bool.
Predicate<int> isPositive = (x) => x > 0;
Console.WriteLine(isPositive(5));//Print True
Solution 5 - C#
MethodInvoker is one which WinForms developers may use; it accepts no arguments and returns no results. It predates Action, and is still often used when invoking onto the UI thread since BeginInvoke() et al accept an untyped Delegate; although Action will do just as well.
myForm.BeginInvoke((MethodInvoker)delegate
{
MessageBox.Show("Hello, world...");
});
I'd also be aware of ThreadStart and ParameterizedThreadStart; again most people will substitute an Action these days.
Solution 6 - C#
Predicate, Func and Action are inbuilt delegate instances of .NET. Each of these delegate instances could refer or point to user methods with specific signature.
Action delegate - Action delegate instances could point to methods that take arguments and returns void.
Func delegate - Func delegate instance could point to method(s) that take variable number of arguments and return some type.
Predicate - Predicates are similar to func delegate instances and they could point to methods that take variable number of arguments and return a bool type.
Solution 7 - C#
Action and Func with lambda:
person p = new person();
Action<int, int> mydel = p.add; /*(int a, int b) => { Console.WriteLine(a + b); };*/
Func<string, string> mydel1 = p.conc; /*(string s) => { return "hello" + s; };*/
mydel(2, 3);
string s1= mydel1(" Akhil");
Console.WriteLine(s1);
Console.ReadLine();
Solution 8 - C#
Func is more LINQ friendly, can be passed in as a parameter. (point-free)
Predicate cannot, has to be wrapped again.
Predicate<int> IsPositivePred = i => i > 0;
Func<int,bool> IsPositiveFunc = i => i > 0;
new []{2,-4}.Where(i=>IsPositivePred(i)); //Wrap again
new []{2,-4}.Where(IsPositivePred); //Compile Error
new []{2,-4}.Where(IsPositiveFunc); //Func as Parameter
Solution 9 - C#
Action
Documentation: > Encapsulates a method that has no parameters and does not return a value.
Functions passed using the Action
type must return void
, and can accept from 0 to 16 parameters.
Action
is commonly used when you want a function that can use each element in a container for something, or as callbacks.
public void DoSomething(List<string> lines, Action<string> action)
{
foreach (string str in lines)
{
action(str);
}
}
Func
Documentation: > Encapsulates a method that has no parameters and returns a value of the type specified by the TResult parameter.
Functions passed using the Func
type accept from 0 to 16 parameters, and return any type that isn't void
.
Func
is commonly used when you want a function that can modify or perform some kind of operation on each element in a container.
// Parameter Types
// ▼▼▼ ▼▼▼
public List<int> DoMath(List<(int, int)> numbers, Func<int, int, int> operation)
// ▲▲▲
// Return Type
{
List<int> results = new();
foreach (var (left, right) in numbers)
{
out.Add(operation(left, right));
}
return out;
}
Predicate
Documentation: > Represents the method that defines a set of criteria and determines whether the specified object meets those criteria.
Functions passed with the Predicate
type must return a bool
, and must accept exactly 1
parameter.
An example of functions that accept Predicates are LINQ functions like All
& Any
, which return true if the supplied predicate returns true for all/any of the elements in a list.
public void Example(string str)
{
// The char.IsLetter() function can be used as a predicate
// because it takes 1 char as a parameter, and returns a bool.
// ▼▼▼▼▼▼▼▼▼▼▼▼▼
if (str.All(char.IsLetter))
{
Console.WriteLine("All characters in the string are letters.");
}
}
Comparison
Type | Return Type | Min Parameters | Max Parameters |
---|---|---|---|
Action |
void |
0 |
16 |
Func |
Any type that isn't void |
0 |
16 |
Predicate |
bool |
1 |
1 |
The only differences between Action
, Func
, and Predicate
are the types that they return & accept as parameters; they are all delegates and as such they all represent a function passed as a parameter.
You can make your own delegate
types in 1 line of code, and use them in the same way as the premade ones.
// Accepts functions that return a decimal type, and...
// ▼▼▼▼▼▼▼
public delegate decimal Operation(decimal left, decimal right);
// ▲▲▲▲▲▲▲ ▲▲▲▲▲▲▲
// ...take 2 parameters, both of type decimal.
You can read more about delegates here:
https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/