Can someone provide a good explanation (hopefully with examples) of these 3 most important delegates:
- Predicate
- Action
- Func
Can someone provide a good explanation (hopefully with examples) of these 3 most important delegates:
Predicate
: essentially Func<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>
: Like IComparer<T>
but in delegate form.
EventHandler/EventHandler<T>
appear all over outside of WinForms too. –
Oloughlin 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>
.
In addition to Jon's answer, there is also
Converter<TInput, TOutput>
: It's essentially Func<TInput, TOutput>
, but with semantics. Used by List.ConvertAll and Array.ConvertAll, but personally haven't seen it anywhere else.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
Predicate<T>
was introduced in .NET 2.0 with the introduction of generics. It's a delegate taking one parameter and returning a bool
.
However, with the introduction of LINQ in .NET 3.5, a need was identified for two families of generic types - Func
and Action
(the difference being on whether they return anything) taking up to 16 41 generic input parameters and being generic in their return types. If Func
had existed first, Predicate<T>
would never have been created in the first place. It's an unnecessarily specialized delegate type.
For backwards compatibility reasons though, they cannot now remove Predicate<T>
from the framework. Some may argue that its name does convey specific semantic meaning but I'd struggle to identify many situations where any Func<T,bool>
(or Func<T1,T2,bool>
, etc.) wouldn't be considered a predicate.
14 in .NET 3.5, 16 in .NET 4 and later.
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.");
}
}
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://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/delegates/
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.
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.
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();
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
© 2022 - 2024 — McMap. All rights reserved.
System.Converter<TInput, TOutput>
, though it's rarely used. – Resistant