In C#, delegates are a fundamental concept used to create references to methods, like function pointers in other programming languages. Delegates provide a way to represent and invoke methods dynamically at runtime. They are primarily used for implementing events, callbacks, and other scenarios where you need to pass methods as arguments or store them as data.
In C#, Action, Func, and Predicate are built-in delegate types that provide a convenient way to work with methods as first-class objects, especially when dealing with functional programming, LINQ, and other scenarios. In this blog post, we will delve into each of these concepts accompanied by illustrative examples.
The source code can be downloaded from GitHub.
Action
The Action delegate is used to represent methods that take parameters but do not return a value (i.e., methods with a void return type). It's commonly used when you want to encapsulate an operation without expecting any result. The Action delegate can take multiple parameters, and its syntax is as follows:
Action<ParameterType1, ParameterType2, ...> actionName = MethodName;
Example
Suppose you want to create an Action delegate to print the sum of two integers to the console. Here's how you can do it:
internal class ActionDelegate
{
public static void TestActionDelegate()
{
Action<int,int> printSum = (a,b) => Console.WriteLine(a+b);
printSum(10,10);
}
}
In this example, printSum is an Action that takes two integers as parameters and prints their sum to the console when invoked.
Func Delegate
The Func delegate is used to represent methods that take parameters and return a value. It's a versatile delegate that can be used for methods with different numbers of parameters and return types. The last type parameter in the Func delegate specifies the return type of the method. The syntax for defining a Func delegate is as follows:
Func<ParameterType1, ParameterType2, ..., ReturnType> funcName = MethodName;
Example
Suppose you want to create a Func delegate that calculates the sum of two integers and returns the result. Here's how you can do it:
public static void TestFuncDelegate()
{
Func<int, int, int> sum = (a, b) => a + b;
int total= sum(20, 20);
Console.WriteLine(total);
}
In this example, sum is a Func that takes two integers as a parameter and returns its sum.
Predicate Delegate
The Predicate delegate is specifically designed for methods that take a single parameter and return a boolean value. It's often used for conditions or filtering. The Predicate type is essentially a Func that takes one argument and returns a boolean. The syntax for defining a Predicate delegate is as follows:
Predicate<ParameterType> predicateName = MethodName;
Example
Suppose you want to create a Predicate delegate that checks if an integer is even. Here's how you can do it:
public static void TestPredicateDelegate()
{
Predicate<int> isEven = (a) => a % 2 == 0;
// Using the Predicate to filter a list of numbers
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6 };
List<int> evenNumbers = numbers.FindAll(isEven);
Console.WriteLine(string.Join(", ", evenNumbers));
}
In this example, isEven is a Predicate that takes an integer and returns true if it's even and false if it's odd. It is then used to filter a list of numbers, resulting in a list of even numbers.
These three delegates (Action, Func, and Predicate) are valuable tools in C# that make it easier to work with methods as first-class objects, enabling you to write cleaner and more expressive code for a wide range of scenarios. They are commonly used in functional programming, LINQ queries, and event handling.