The C# delegate is an essential “construct” in the C# programming language. Delegates are essential for event handling, LINQ queries, asynchronous programming and more. And you can, of course, make use of delegates to make your code simpler and more concise.
This post offers you a guide to this incredibly useful tool in C#. By the end of the post, you’ll have learned:
Let’s get started.
A C# delegate is an object that represents a method. The C# delegate allows you to treat a method as a value, assigning the method to a variable, passing it to other methods as parameters, adding it to a collection, and so on. Delegates are similar—regarding their behavior—to what some other languages call a function pointer, the difference being that delegates are fully object-oriented.
As a next step, let’s understand why you’d need delegates.
A common scenario in programming is when the part of the code that knows the action that needs to be executed isn’t the same part of the code that performs the execution. In such situations, you need a way to encapsulate an action inside an object and pass it around.
How to solve this issue? C# delegate to the rescue! By instantiating a delegate, you can express an action as an object and hand that object over (or delegate it) to the code that’s actually able to execute the action.
In C#, delegates are particularly useful for event handling. Through delegates, you subscribe to an event. Delegates are also essential in LINQ, which is honestly a big part of what makes programming in C# so pleasurable.
Let’s declare a delegate type that represents a function that takes a string as a parameter and returns an integer:
public delegate int StrToInt(string s);
The line above is a delegate declaration. We essentially defined a new type. Now, we can create delegate instances that match that declaration. Consider we have the following method:
public static int ConverToNumber(string s)
{
if (int.TryParse(s, out int result))
return result;
return 0;
}
Let’s say we also have this method:
public static int GetLength(string text)
{
return text.Length + 10;
}
Not the two most useful methods ever written, but both fit the bill. That is to say, both match the definition of our delegate, so we can assign them to instances of that type:
StrToInt myAction = ConvertToNumber;
StrToInt otherAction = GetLength;
Now, we can use the delegate instances to execute the actions:
var someText = "Hello World!";
var length = otherAction.Invoke(someText);
Console.WriteLine($"The text '{someText}' has a length of {length}.");
The code above uses the Invoke method to run the method encapsulated by the delegate instance. You can also just call a delegate the same way you would a normal method:
var numberAsText = "10";
var number = myAction(numberAsText);
In the examples above, we’ve assigned “normal” methods to the delegate instances. These assignments work just fine and might be a good option if you already have the right method defined. But sometimes you don’t, and having a concise syntax to quickly instantiate a delegate instance can be a lifesaver. Anonymous methods come in handy in this scenario:
StrToInt myAction = delegate(string s) { return s.Length; };
As you can see, it’s possible to use the delegate keyword to create a function on the fly and assign it to the delegate instance. But why stop there? We can go a step further:
StrToInt myAction = s => s.Length;
In the example above, we use a lambda expression to define a function that gets converted to a delegate instance. You’re probably used to seeing lambdas around, as they’re certainly the most common way to instantiate delegates.
You’ve seen the what and why of C# delegates, and even some examples. Let’s now go deeper into the “how.”
You can leverage different types of delegates in C#. The ones you’ve seen so far are single-cast: delegates that point to a single method. Having a delegate hold two or more methods is possible and often quite valuable.
Delegates that hold more than one method are \ called multicast delegates. You can use the plus (+) operator to add more methods to a delegate instance, and the minus (-) operator to remove a method. When you invoke the delegate instance, it executes all of the methods in its invocation likes in the order in which they were added.
Let’s see a multicast delegate in action. First, consider the following delegate declaration:
public delegate void DisplaySomething(int number);
Let’s now instantiate this type and use a lambda expression to create an anonymous method that displays a message showing whether the number is even or odd:
// yep, I know about bitwise operators, wrote like this for readability
DisplaySomething displayEvenOrOdd = x => Console.WriteLine(x % 2 == 0 ? "even" : "odd");
Now we’ll create another instance that takes a number and displays its value in binary:
DisplaySomething displayBinary = x => Console.WriteLine(Convert.ToString(x, 2));
Let’s now create yet another instance that displays as many asterisks as the number informed:
DisplaySomething displayNAsteriks = x => Console.WriteLine(new String('*', x));
Finally, let’s combine all three into a multicast delegate and invoke it:
DisplaySomething displayAll = displayEvenOrOdd + displayBinary + displayNAsteriks;
displayNAsteriks;
displayAll(10);
And voilá:
even
1010
**********
Now, let’s remove the “display binary” function from the invocation list and invoke the multicast delegate again:
displayAll -= displayBinary;
displayAll(9);
And this is what we get:
odd
**********
As it turns out, working with delegates can be even easier than what we’ve shown you up to now. That’s because .NET offers some built-in generic delegates that can spare you the work of having to declare your own delegate types. Let’s explore them right now.
The first generic delegate in our list is Func<T, TResult>. We can use it to quickly define a delegate that gets a parameter (expressed by T) and returns a value (defined by TResult.) Recall our first declaration example:
public delegate int StrToInt(string s);
Using Func, we wouldn’t need to declare that type and could’ve gone straight away with the instantiation:
Func<string, int> myAction = word => word.Length;
How to use this? Same as before: use the Invoke method, or simply call it the same way you call a regular method:
var result = myAction("C# is awesome!");
You can, of course, declare methods with more than one parameter. For instance, one that takes two ints and returns an int:
Func<int, int, int> sum = (a, b) => a + b;
What about a method with no argument but that returns a value? No problem:
Func<DateTime> whatTimeIsIt = () => DateTime.Now;
What if you need to declare a void method? Then Action<T> is your friend:
Action<int> displayEvenOrOdd = x => Console.WriteLine(x % 2 == 0 ? "even" : "odd");
And to declare a void method with no arguments, simply use Action with no type parameters:
Action displayHelloWorld = () => Console.WriteLine("Hello World!");
Finally, let’s cover Predicate<T>. In a nutshell, this generic delegate is for methods that perform a kind of check, based on some criteria, and return either true or false. Suppose you have a list of numbers:
var numbers = Enumerable.Range(1, 10).ToList();
You want to get a list with just the even numbers. You can use a Predicate to define a method that does the checking:
Predicate<int> isEven = x => x % 2 == 0;
Then, as a next step, you could use Func to define a function that does the filtering:
Func<List<int>, Predicate<int>, List<int>> filter = (input, predicate) =>
{
var result = new List<int>();
foreach (var i in input)
{
if (predicate(i)) result.Add(i);
}
return result;
};
Of course, in real life, you wouldn’t write code like this and would use the Where LINQ extension method instead. Interestingly, the Where LINQ delegate uses Func<T, Boolean> instead of Predicate<T>.
In this post, we’ve offered you a comprehensive introduction to C# delegates. As you’ve seen, they’re a powerful part of programming in C#, which can help you write simpler, more concise code.
For instance, a few days ago, I was struggling with code duplication in a codebase at work. I had the same exception-handling logic popping up in a lot of places. That’s when I decided to leverage the power of delegates. I extracted the exception handling code to a dedicated method and then used Func in order to pass the action to be executed to that method. While just an example from work, I’m sure you’ll find plenty of opportunities to use delegates to improve your code.
Before departing, a final suggestion. If you want to continue your C# learning journey, the Stackify blog is a great place to hang out. With plenty of resources for you to improve your .NET developer skills, grab a coffee, look around, learn and enjoy!
This post was written by Carlos Schults. Carlos is a consultant and software engineer with experience in desktop, web, and mobile development. Though his primary language is C#, he has experience with a number of languages and platforms. His main interests include automated testing, version control, and code quality.
If you would like to be a guest contributor to the Stackify blog please reach out to [email protected]