Using Delegates in C#
Using a delegate in programming is similar to delegating a task in real life. Just like you might delegate the task of washing your clothes to someone else who has the necessary skills to complete it, in programming, you can delegate the task of performing a certain method to another method called a delegate.
To understand delegates better, let’s first look at what a delegate is. At its core, a delegate is a pointer to a function. It represents what parameters a method accepts and the value it returns, also known as its signature. The most important feature of a delegate is that it provides a way to refer to a method by this signature without invoking the method directly.
Delegates can help reduce code duplication by allowing a single method to be reused with different behavior depending on the provided delegate. For example, you may have a method that performs a certain task, but you don’t want to call that method directly every time you need that task performed. Instead, you can delegate the task to another method by passing a reference to the original method as a parameter. This allows you to reuse the same code in different parts of your program and can help you write more efficient and maintainable code.
In this article, we will explore how to implement and use a delegate in C# using a simple example. Let’s say we have a method called Addition()
that takes in two integers a
and b
and returns their sum. Its signature would beint Addition(int a, int b).
To create a valid delegate for this method, we need to define an AdditionDelegate with a matching signature that also takes in two integers and returns an integer. It is important to note that if we try to use a delegate with a different signature to interact with the Addition()
method, we will encounter errors.
//Method Addition Takes in 2 parameters a and b and returns their sum
public int Addition(int a, int b)
{
return a + b;
}
//Addition Delegate matching the signature of the Addition method
public delegate int AdditionDelegate(int a, int b); //Correct for Addition method
//Wrong: return type invalid for Addition method
//public delegate void AdditionDelegate1(int a, int b);
// Wrong: Only takes one argument, Addition method takes 2 arguments
//public delegate int AdditionDelegate2(int a);
To use our shiny new delegate, we instantiate it and pass the method Addition as an argument. Note that we are only passing a reference to the method and should not call the function. In addition, attempting to pass a method such as AddDouble (which takes in a single integer and returns the integer plus itself) to the addDelegate would cause issues as methods and delegates are only compatible if their signatures match.
// Create a delegate instance that points to the Add method
AddDelegate addDelegate = new AddDelegate(Addition); //Correct
//Calling the method like this is wrong and defeats the purpose of delegates
//AddDelegate addDelegate = new AddDelegate(Addition());
public int AddDouble(int a)
{
return a + a;
}
// Wrong: Trying to pass the AddDouble to will lead to problems
// AddDelegate addDelegate2 = new AddDelegate(AddDouble);
Now, instead of directly calling the Addition method every time, we could use the delegate instead.
// Call the Add method through the delegate
int result = addDelegate(5, 7);
Console.WriteLine($"The sum of the numbers is {result}");
Another advantage of using a delegate is that we can now use the Addition()
method as a callback function, which means it can be passed as an argument to another function. For example, if we wanted to create a ArrayOperation()
method that takes an array of integers and adds the index of each integer in the array to the integer, we can pass a reference to the Addition()
method. In this way, the ArrayOperation()
method can use the delegate to call the Addition()
method for each element in the array and perform the desired operation as shown in the code block below.
namespace SimpleDelegates
{
//Defining delegate for Addition Operation Method in AdditionOperation Class
public delegate int AdditionDelegate(int a, int b);
public class AdditionOperation
{
public int Addition(int a, int b)
{
return a + b;
}
}
public class Program
{
static void Main(string[] args)
{
//Function that takes in an instance of add delegate and an array and returns updated array
static int[] ArrayOperation(AdditionDelegate addDelegate, int[] arr)
{
for (int i = 0; i < arr.Length; i++)
{
addDelegate(i, arr[i]); //use the add delegate to add each integer with its position in the array
}
return arr;
}
AdditionOperation additionOperation = new AdditionOperation();
AdditionDelegate additionDelegate = new AdditionDelegate(additionOperation.Addition);
int[] arr = new int[] { 1, 2, 3, 4 };
int[] updatedArray = ArrayOperation(additionDelegate, arr);
foreach (int num in updatedArray)
Console.WriteLine(num);
}
}
}
Although this is a simple example, delegates can be used for more intricate scenarios. In the next post, we will delve into the usage of generic delegates to create type-flexible delegates, making our code more adaptable and reusable.