Delegates em C#
Delegates
are used to refer to methods.
In the same way that int
is used for integers and string
for text, we use delegate
to refer to methods.
In practice
Consider the following code:
delegate string? Serialize(object value, JsonSerializerOptions? options = null);
delegate object? Deserialize(string value, Type returnType, JsonSerializerOptions? options = null);
class BusinessSerializer
{
Serialize PointerToExternalMethodSerialize { get; }
Deserialize PointerToExternalMethodDeserialize { get; }
AnyClass(Serialize referenceToSomeSerializeMethod, Deserialize referenceToSomeDeserializeMethod)
{
PointerToExternalMethodSerialize = referenceToSomeSerializeMethod;
PointerToExternalMethodDeserialize = referenceToSomeDeserializeMethod;
}
string? Serialize(object value) => PointerToExternalMethodSerialize(value);
object? Deserialize(string value, Type returnType) => PointerToExternalMethodDeserialize(value, returnType);
}
The BusinessSerializer
class declares the Serialize
and Deserialize
methods, but their implementation depends on the delegates received in the constructor.
using System.Text.Json;
AnyClass anyObject = new AnyClass(JsonSerializer.Serialize, JsonSerializer.Deserialize);
Action, Func and Predicate
Delegate is extremely versatile as we can reference any method with it.
To make the code more semantic we can use 3 specialized types of delegates: they’re Action
, Func
and Predicate
.
Action
public delegate void Action<in T>(T obj);
This delegate represents an action that has no return (void
).
Method arguments can be specified using Type Parameters
.
Action<string> WriteToSink = Console.WriteLine;
WriteToSink("This message will be displayed in the Console.");
Action
are commonly used in Xamarin.Forms/.Net MAUI to represent Commands
(events).
Function
public delegate TResult Func<in T,out TResult>(T arg);
This is the most generic among the three.
It is possible to specify all parameters and return using Type Parameters
.
static double Divide(int dividend, int divisor) => dividend / divisor;
Func<int, int, double> DivideMethod = Divide;
DivideMethod(21, 7); // Returns 3.
DivideMethod
, effectively being a pointer to a method, will execute Divide
when called.
Predicate
public delegate bool Predicate<in T>(T obj);
A predicate is a method that tests one or more conditions and always returns bool
.
It is possible to specify all parameters types using Type Parameters
.
static bool IsEvenNumber(int number) => number % 2 == 0;
Predicate<int> NumberCheck = IsEvenNumber;
if(NumberCheck(6)) // Evaluates to true.
{
// Process the even number.
}
Predicates have the limitation of one input parameter.
TL;DR
While the delegates
type is flexible and applicable in any situation, this is also its downside, as one has to parse its signature to understand its usage.
Action
, Func
and Predicate
allow you to demonstrate intent more clearly by categorizing the types of delegates
into actions, methods with arbitrary return and methods that test a condition.