Learn how you can take advantage of delegates like Action, Func, and Predicate to facilitate callbacks and add flexibility to your code. Credit: sirtravelalot While passing objects as arguments is a standard and familiar way to invoke methods, providing methods as arguments to other methods is less so. Nonetheless, we often must pass a method as a parameter to another method when working with event handling in C#. We do this using delegates. I provided an overview of delegates in an earlier article here. In this article, we’ll examine how we can work with Action, Func, and Predicate delegates in C#. To work with the code examples provided in this article, you should have Visual Studio 2022 installed in your system. If you don’t already have a copy, you can download Visual Studio 2022 here. A delegate is a type-safe function pointer that can reference a method that has the same signature as that of the delegate. Delegates are used to define callback methods and implement event handling, and they are declared using the “delegate” keyword. You can declare a delegate that can appear on its own or even nested inside a class. What are Func and Action delegates? How can they be used? The most common delegates in C# are the Func delegate and the Action delegate. Both are reference types that encapsulate a method. The Func delegate points to a method that accepts parameters and returns a value; the Action delegate points to a method that accepts parameters but does not return a value (i.e., returns void). Both of these delegate objects will take zero or many parameters, and we can use them with lambda expressions or anonymous methods. The syntax for creating an action delegate in C# is Action<TParameter> We can create an Action delegate in C# using the Action keyword. Action<string> actionDelegate = new Action<string>(DisplayText); actionDelegate("Hello World!"); The syntax for declaring a Func delegate in C# is Func<TParameter, TOutput> To create a function delegate in C#, we use the Func keyword. public class DelegateHelper { Func<double, double> functionDelegate = new Func<double, double>(GetTax); public static double GetTax(double netSalary) { return (double)(netSalary * .3); } } Console.WriteLine(DelegateHelper.GetTax(100000)); Here the DelegateHelper class contains a static method named GetTax, which calculates and returns tax based on net salary, and a delegate that points to this method. A Func delegate has been used to invoke the GetTax method. Using Action delegates in C# The following code listing provides another example of how you can use an Action delegate. This code snippet when executed would print the word “Hello!!!” in the console window. static void Main(string[] args) { Action<string> action = new Action<string>(Display); action("Hello!!!"); Console.Read(); } static void Display(string message) { Console.WriteLine(message); } Using Func delegates in C# And here is a second example of using a Func delegate in C#. The following code snippet prints the value of a health reimbursement account, or HRA (calculated as 40% of basic salary). The base salary is passed to the delegate as an argument. static void Main(string[] args) { Func<int, double> func = new Func<int, double>(CalculateHra); Console.WriteLine(func(50000)); Console.Read(); } static double CalculateHra(int basic) { return (double)(basic * .4); } Note that the second parameter in the declaration of the Func delegate in the code snippet given earlier represents the return type of the method to which the delegate would point. In this example, the calculated Hra value is returned as double. Using Predicate delegates in C# A Predicate is a delegate that accepts one or more generic parameters and returns a boolean value. Predicate delegates are typically used to perform search operations based on a set of criteria. Here is the syntax for a Predicate delegate. Predicate<T> Note that Predicate is basically equivalent to Func. Consider the following entity class named Customer. class Customer { public int Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Address { get; set; } public string City { get; set; } public string State { get; set; } public string Country { get; set; } } Next, create a list of customers and store objects of type Customer into it. List<Customer> custList = new List<Customer>(); custList.Add(new Customer { Id = 1, FirstName = "Joydip", LastName = "Kanjilal", State = "Telengana", City = "Hyderabad", Address = "Begumpet", Country = "India" }); custList.Add(new Customer { Id = 2, FirstName = "Steve", LastName = "Jones", State = "OA", City = "New York", Address = "Lake Avenue", Country = "US" }); The following is the complete code listing that shows how we can use a Predicate delegate to search data. static void Main(string[] args) { List<Customer> custList = new List<Customer>(); custList.Add(new Customer { Id = 1, FirstName = "Joydip", LastName = "Kanjilal", State = "Telengana", City = "Hyderabad", Address = "Begumpet", Country = "India" }); custList.Add(new Customer { Id = 2, FirstName = "Steve", LastName = "Jones", State = "OA", City = "New York", Address = "Lake Avenue", Country = "US" }); Predicate<Customer> hydCustomers = x => x.Id == 1; Customer customer = custList.Find(hydCustomers); Console.WriteLine(customer.FirstName); Console.Read(); } When the above code snippet is executed, the name “Joydip” will be displayed in the console window. Using variance in delegates in C# When you define your delegates, you can use covariance and contravariance to give your delegate a greater level of flexibility. The covariance approach makes it possible for a method to return a more derived type than that defined in the delegate. Using contravariance, you can define a method that accepts less derived arguments than those in the delegate type. The following code snippet illustrates covariance in delegates. class TypeA {} class TypeB : TypeA {} public delegate TypeA MyDelegate(); public static TypeA HandlerA() { //Some code } public static TypeB HandlerB() { //Some code } MyDelegate delegateA = HandlerA; MyDelegate delegateB = HandlerB; Now, consider the following two delegate declarations. public delegate void KeyEventHandler(object sender, KeyEventArgs e) public delegate void MouseEventHandler(object sender, MouseEventArgs e) Because EventArgs is the base class of both KeyEventArgs and MouseEventArgs, you can take advantage of contravariance to combine them into just one handler that is capable of handling both key and mouse events as shown in the code snippet given below. private void MyCommonHandler(object sender, System.EventArgs e) { //Some code } Delegates are among the most widely used features of the C# programming language. They are ideally suited to implementing event-driven programming. In most cases, the return type of the method pointed to by a delegate must be identical to the type specified in the delegate’s declaration. However, we can turn to covariance and contravariance to gain more flexibility. Related content news Microsoft unveils imaging APIs for Windows Copilot Runtime Generative AI-backed APIs will allow developers to build image super resolution, image segmentation, object erase, and OCR capabilities into Windows applications. By Paul Krill Nov 19, 2024 2 mins Generative AI APIs Development Libraries and Frameworks news Akka distributed computing platform adds Java SDK Akka enables development of applications that are primarily event-driven, deployable on Akka’s serverless platform or on AWS, Azure, or GCP cloud instances. By Paul Krill Nov 18, 2024 2 mins Java Scala Serverless Computing news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks how-to How to use DispatchProxy for AOP in .NET Core Take advantage of the DispatchProxy class in C# to implement aspect-oriented programming by creating proxies that dynamically intercept method calls. By Joydip Kanjilal Nov 14, 2024 7 mins Microsoft .NET C# Development Libraries and Frameworks Resources Videos