This article discusses an important topic about delegated types in C # (csharp).
It will undoubtedly be part of a series of articles on the basics of CSharp.
So we’ll see what delegates are and why they’re so important in .NET application development.
What is a delegate type?
In fact, a delegate type is a very simple concept: it is a type that allows you to reference a method of a class.
Thanks to delegates, you can pass a method as parameter (it’s a bit like the function pointers in C / C ++ for example).
A delegate type is always associated with the signature of a method.
Only methods with the same signature can be used with this delegate.
delegate is also a keyword in the C # language (csharp) that allows you to declare a delegate.
To declare a delegate in CSharp, here’s what to do:
- In your class, you use the delegate keyword followed by the signature of the associated method,
- The name of the method indicated in the signature will be the name of your delegate,
- You can optionally add a visibility indicator at the beginning (private, public or protected for example).
Here’s an example code that allows you to declare a delegate in C #:
1 2 3 4 5 6 | public class MyClass { public delegate int Calculator(List<int> elements); } |
In this example, I declare a delegate for a method that takes an int list and returns an int.
All the methods corresponding to this signature can be used with this delegate.
How to use a delegate?
After declaring a delegate type, you must now use your delegate in your CSharp code.
A delegate type can be used like any type in C #: in methods (input arguments, return parameter) or in classes (property or member).
Here is an example of a delegate type usage in C # (following the previous example):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | int MaMethodeDeCalcul(List<int> elements) { return elements.Count; } void Main() { MaClasse.Calculer delegateCalculer = new MaClasse.Calculer(MaMethodeDeCalcul); MaClasse instance = new MaClasse(); // here, I call the Treat MyClass method by passing // the delegate as an argument. instance.Traiter(delegateCalculer); // you could have written this: // instance.Treat (new MyClass.Calculate (MyCalculatedMethod)); // or even this (since the last versions of C #): // instance.Treat (MyCalculatedMethod); } |
Ok, so far, you’ll tell me: well, but what’s the use of a delegate?
The most common use of delegate types
C # delegate types are used to pass methods as arguments during calls.
It is therefore possible to create different behaviors for the same code.
Indeed, delegate types are extension points of your code.
It will be possible to change the behavior of a piece of code simply by passing another delegate.
Here is a very simple example:
- You have a class that performs calculations on a series of numbers,
- A “Calculate” method takes as argument a list of integers and two delegates: BeforeProcessingDelegate and AfterDelegationDelegate,
- These two delegates have the function of being able to filter or modify a series of elements before or after a treatment,
- The Calculate method can be called several times in your application with references to different methods.
To better understand, I propose an example that shows two different calls:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | public MaClasseDeTraitement { public delegate void AvantTraitementDelegate(List<int> elements); public delegate List<int> ApresTraitementDelegate(List<int> elements); // Simple calculation method (invert list) public List<int> Calculer(List<int> elements, AvantTraitementDelegate avant, ApresTraitementDelegate apres) { List<int> copie = new List<int>(elements); // delegate call if defined if (avant != null) avant(copie); copie.Reverse(); // delegate call if defined if (apres != null) copie = apres(copie); return copie; } } // This method adds one to each element void AjouterUn(List<int> elements) { for (int i = 0; i < elements.Count; i++) elements[i] = elements[i] + 1; } // This method removes the 0 from the list List<int> SupprimerZero(List<int> elements) { elements.Remove(0); return elements; } void Main() { MaClasseDeTraitement traitement = new MaClasseDeTraitement(); List<int> elements = new List<int>(); // TODO: fill in the list here // Here, I call Calculate by passing the elements but without delegate List<int> resultat1 = traitement.Calculer(elements, null, null); // This time, we make the call but passing two delegates: // The first performs a pre-processing (add one to each element), // the second remove the 0 from the list. List<int> resultat2 = traitement.Calculer(elements, AjouterUn, SupprimerZero); } |
As you can see, it is possible to do different operations depending on the methods passed in parameter.
- In the first case, we call Calculate without a method of treatment.
- In the second case, we call Calculate with both methods of treatment, which will produce a completely different result.
Why are delegates so important?
Delegate types are important in C # for two main reasons:
First, because they are used everywhere in the Dotnet framework. It is therefore very important to understand this concept to use the .NET framework correctly.
How to go further ?
If you want to use the .NET Framework optimally, I strongly advise you to practice and try to use and then create your own delegates.
That’s why I have prepared, as a bonus for this article, a method sheet that summarizes the essentials and offers exercises to go further in practice.