The first programming language I learned was JavaScript.
A bit further down the track, I started learning C# because of a job opportunity.
As you might know, moving from a scripting dynamically typed language to a strongly typed one, has some major differences. The concept of Delegates is one piece of syntax that made a bunch of confusion in my head.
It seemed far too complex and something I hadn't seen before (which was not the case).
This is how I managed to understand C# delegates.
First what is a Delegate?
The standard answer is that a Delegate is a pointer to a function or a variable that holds the location of a function in memory.
More precisely, a delegate is a variable that holds a pointer to a function with a certain signature.
Just like when creating a method you have to specify the parameter types and the return type of the method, when creating a variable that holds a pointer to a method/function, the type of that variable has to contain the parameter types that method/function accepts and the return type.
The question is what is the type to use when declaring a delegate and how do we declare one?
There are system-defined delegate types (Action, Func, and Predicate) and you can also create your own.
Let us focus on the system-defined ones which make understanding much simpler.
The Action type is used when your delegate(read function) won't have a return(returns void).
It accepts up to 16 parameters! (Why create your own type?)
Action myFunction = delegate (string msg){
Console.WriteLine(msg);
};
//OR Using Lambda
Action myFunction = msg => Console.WriteLine(msg);
The Func type is used when the function returns anything other than void.
Let's declare a variable that holds a pointer to a function that takes 2 numbers and returns their sum:
//first 2 parameters of the Func<> type are the function's arguments and the third is the return type.
Func<int, int, int> add2Nums = delegate (int num1, int num2){
return num1 + num2;
}
//or
Func<int, int, int> add2Nums = (num1, num2) => num1 + num2;
Notice the syntax resemblance with JavaScript, if you replace C#'s delegate keyword with javaScript's function keyword, the code will look exactly the same.
//as JavaScript is not strongly typed, you don't have to worry about the type of the variable you are declaring, a `let` or `var` can hold any value, be it int, decimal, string, objects or functions, etc.
let add2Nums = function (num1, num2) {
return num1 + num2;
}
//or
let add2Nums = (num1, num2) => num1 + num2;
Last is Predicate is a system-defined type of delegate that takes a single type (T which can be any type, string/int/Class primitive or reference type) and returns a boolean.
Let us have a look at an example:
//Predicates are commonly used on lists/arrays for filtering/sorting.
//create an array of ints
int[] intsArray = { 1, 2, 3, 4, 5, 6, 8, 9, 10 };
//declare predicate delegate
Predicate<int> checkIfMoreThanFive = x => x > 5;
var onlyGreaterThanFive = Array.FindAll(intsArray, checkIfMoreThanFive);
//same can be achieved without declaring the delegate and using the arrow function directly as a parameter to the `FindAll` method
var onlyGreaterThanFive = Array.FindAll(intsArray, x => x > 5);
See that the second parameter expected by the FindAll
method is of type Predicate
. More on Predicates in a future post.
As I mentioned above you can also define your own (not sure why you would bother, but you would find them on pretty much every tutorial out there on the topic, which was what made it confusing to me in the first place)
Let's see it.
//delegate with a signature of a function that takes a single string parameter and has no return.
//same as Action<string>
public delegate void MyCustomDelegateType(string msg);
MyCustomDelegateType myFunction = delegate (string msg)
{
Console.WriteLine(msg);
};
//or
MyCustomDelegateType myFunction1 = msg => Console.WriteLine(msg);
Now that we know what a delegate is and how you can declare one, let's check what is the most common use case for delegates.
In most cases, a delegate(again, read function) is used as a parameter to another function(known as a callback function, which will be executed at some point inside the function body.
Let's see an example:
public class MyClass
{
//method
public static void LetMeKnowWhenDone(Action callbackFunction){
//do a bunch of stuff here, then call the function
callbackFunction();
}
}
//you can use it like so:
Action<int> myFunction = () => Console.WriteLine("done.");
MyClass.LetMeKnowWhenDone(myFunction);
//or pass the lambda(or regular function) directly into the function parameters
MyClass.LetMeKnowWhenDone(() => Console.WriteLine(x));
//or regular function with a body
MyClass.LetMeKnowWhenDone(delegate () {
Console.WriteLine("done.");
})
And as might have imagined, you can also create your own delegate type inside the class.
public class MyClass
{
//declare your custom delegate type
public delegate void MyCustomDelegateType (); //takes no parameters
//method
public static void LetMeKnowWhenDone(MyCustomDelegateType
callbackFunction){
//do a bunch of stuff here, then call the function
callbackFunction();
}
}
//you can use it like so:
//the delegate type is defined inside the MyClass class
MyClass.MyCustomDelegateType myFunction = () => Console.WriteLine("done.");
MyClass.LetMeKnowWhenDone(myFunction);
//same as examples above for lambdas e regular functions passed directly into the method
Another way to use a delegate inside a class is not only declaring the delegate type but also instantiating the delegate type inside the class, then passing the instance into the method as a parameter.
Let's have a look:
public class MyClass
{
//declare your custom delegate type
public delegate void MyCustomDelegateType (); //takes no parameters
//declare a field of the delegate type you have just declared above.
public MyCustomDelegateType myDelegateInstance;
//pass the delegate field as a parameter to the method
public void LetMeKnowWhenDone(){
//do a bunch of stuff here, then call the function
myDelegateInstance.Invoke(); //to invoke/execute the function
}
}
//you can use it like so:
var myClass = new MyClass(); //I did not make the field and method statics this time
//point the field to any delegate(function) with the same signature
myClass.myDelegateInstance = () => Console.WriteLine("done.");
MyClass.LetMeKnowWhenDone(); // it invokes the function you assigned the delegate instance object with
//same as previous examples for lambdas and regular functions passed directly into the method
These examples are the most simplistic one could possibly think of. And that is the idea, digest and understand one thing well at a time with examples that isolate the idea you are trying to understand and do not introduce more pieces of unknown code.
There is one last thing I want to mention before I wrap up.
If C# is an object-oriented language, and if you want to declare a function, generally what you do is create a method(a function that belongs to a class, at least that is how to distinguish between functions and methods) inside of a class.
And when you want to use that method you instantiate the class and call the method(or call the method through the class if the class is static).
But how then can we declare delegates and lambdas left and right without it having a class that it belongs to? how does it work?
I learned reading the book Pro C# 7, that whenever you use a lambda function or declare a delegate(system defined or that you defined) the compiler automatically creates a sealed class that derives from System.MulticastDelegate (which inherits from System.Delegate) around it.
So there is no such thing as a function floating around with no class in C# like there is in JavaScript.
Enough for today, the next stop will be at C# events (Hint: it is implemented using delegates)
I hope it wasn't too boring.
Until the next one.
Gus.
Top comments (1)
dotnet --version
8.0.201
Action<string> myFunction = delegate (string msg){
Console.WriteLine(msg);
};
//OR Using Lambda
Action<string> myFunction = msg => Console.WriteLine(msg);
Maybe something changed - had to add
<string>
.CS1593