Using Delegates in .NET

 Nov 13, 2014

We can think of the concept of delegates in the .NET framework as a type-safe implementation of callback functions. In today’s post, I’ll dig deeper into this definition and the review this important concept. What we mean by a callback function is just the name of an existing function that we pass as an argument to another function. The use of callback functions is pervasive in C/C++ programming and although very useful, they are the source of a lot of trouble because C/C++ compilers will not check if the callback function is used properly inside the function that receives it as an input parameter. The name of the callback function is nothing more than just an address in memory that will be invoked inside the body of some other function. So if we don't pass to the callback function exactly the type of parameters it is expecting, we will crash our program and we will only know it at run time. In .NET, it's a whole new story because now the callback function becomes a declared type that will be initialised with the function to be called back. When this initialisation happens, the compiler can check if the declaration of the delegate type matches the signature of the function we want to call back. So we can say that delegates are type-safe and they provide the same functionality of callback functions, aka function pointers, in C/C++ unmanaged code. Now let's review the syntax to use delegates in C#. We are choosing C# because it's the most popular language in .NET, but of course VB.NET offers the same functionality. We can declare delegates as a top-level type or inside a class using the delegate keyword:
public delegate void MyDelegate1(int a, int b); public delegate string MyDelegate2(int a, string s);
In the first example, MyDelegate1 will match any function that accepts 2 integers as input and has no return value, as for example:
public void MyFunction1(int x, int y) { ... }
In the second example, we will initialise MyDelegate2 with functions receiving an int followed by a string and returning a string:
public string MyFunction2(int x, string msg) { ... }
The way to initialise the delegate is by declaring a variable with the type of the delegate and then assign to that variable a function with the right signature. MyDelegate1 callback1 = MyFunction1; MyDelegate2 callback2 = MyFunction2; Now, let's say we have some functions that are expecting input parameters with the types MyDelegate1 and/or MyDelegate2 among other parameters:
void Test1(MyDelegate1 p, int a, int b) { p(a,b); //this line is invoking the delegate and will execute whatever we pass in to p } void Test2(MyDelegate2 p, int a, string s) { string result = p(a,s); //it calls the function assigned to p } void Test3(MyDelegate1 p1, MyDelegate2 p2, ... ) { ... }
We will be able to call those 3 functions passing the variables callback1 and callback2 as appropriate Test1(callback1); Test2(callback2); Test3(callback1, callback2); Note that callback1 is referring to MyFunction1, so inside Test1 we are executing MyFunction1. The same for callback2 in Test2. In Test3, we could use both functions. The flexibility in using delegates comes from the fact that we can decide are runtime what functions we will pass to Test1, Test2 and Test3. If we are always passing the same functions then of course, there would be no reason to be using the delegate as an intermediary. Instead, we would just hardcode the call to MyFunction1 inside Test1, MyFunction2 inside Test2, etc. and simply eliminate the first parameter in Test1 and Test2 or the first 2 parameters in Test3. Also note that the only restriction for the function we use to initialise the delegate is that it has the right signature. So it could be defined anywhere, even in a class located in some external assembly we add as a reference. We can write code that will use the functionality provided by some function even before we have that function available. We declare the delegate with the signature of the function and start using variables with the delegate type everywhere that function is required. Once we get hold of some library that contains the implementation for those required functions we can add a reference to the assembly and initialise the delegate variable with the appropriate functions. The code is prepared to work with whatever external library we choose. To learn more about delegates in .NET we recommend the course 20483: Programming in C#.

How do your Excel skills stack up?   

Test Now  

About the Author:

Newton Godoy  

With over 17 years of in-class training experience and over 16 years of industry experience, Newton offers students a wealth of real-world technical knowledge and expertise in the areas of .NET application development, SQL Server and SharePoint Server. After spending several years lecturing as a professor, Newton found his true calling and began his career as a MCT. He worked as a technical trainer for some of Brazil's and Australia’s largest corporate training organisations before finally finding a home with New Horizons where he is now one of our top trainers. Newton brings a thorough mentoring capability to the classroom where he can advise on technical issues and challenges often beyond the scope of the course curriculum. His combination of technical knowledge and instructor experience make him one of the most respected instructors within the IT training industry.

Read full bio
Back to top