Les délégués (en anglais
delegate) en C# ne s’occupent pas de la classe, ni du personnel... Ils permettent de créer des variables spéciales. Ce sont des variables qui « pointent » vers une méthode.
C’est un peu comme les pointeurs de fonctions en C ou C++, sauf qu’ici on sait exactement ce que l’on utilise, car le C# est un langage fortement typé.
Le délégué va nous permettre de définir une signature de méthode et avec lui, nous pourrons pointer vers n’importe quelle méthode qui respecte cette signature.
En général, on utilise un délégué quand on veut passer une méthode en paramètres d’une autre méthode.
Un petit exemple sera sans doute plus parlant qu’un long discours. Ainsi, le code suivant :
Code : C# | public class TrieurDeTableau
{
private delegate void DelegateTri(int[] tableau);
}
|
crée un délégué privé à la classe
TrieurDeTableau qui permettra de pointer vers des méthodes qui ne retournent rien (
void) et qui acceptent un tableau d’entier en paramètres.
C’est justement le cas des méthodes
TriAscendant() et
TriDescendant() que nous allons rajouter à la classe (ça tombe bien !) :
Code : C# 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 | public class TrieurDeTableau
{
private delegate void DelegateTri(int[] tableau);
private void TriAscendant(int[] tableau)
{
Array.Sort(tableau);
}
private void TriDescendant(int[] tableau)
{
Array.Sort(tableau);
Array.Reverse(tableau);
}
}
|
Vous aurez compris que la méthode
TriAscendant utilise la méthode
Array.Sort pour trier un tableau par ordre croissant. Inversement, la méthode
TriDescendant() trie le tableau par ordre décroissant en triant par ordre croissant et en inversant le tableau ensuite.
Il ne reste plus qu’à créer une méthode dans la classe permettant d’utiliser le tri ascendant et le tri descendant, grâce à notre délégué :
Code : C# 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 | public class TrieurDeTableau
{
[…Code supprimé pour plus de clarté…]
public void DemoTri(int[] tableau)
{
DelegateTri tri = TriAscendant;
tri(tableau);
foreach (int i in tableau)
{
Console.WriteLine(i);
}
Console.WriteLine();
tri = TriDescendant;
tri(tableau);
foreach (int i in tableau)
{
Console.WriteLine(i);
}
}
}
|
Nous voyons ici que dans la méthode
DemoTri nous commençons par déclarer une variable du type du délégué
DelegateTri, qui est le délégué que nous avons créé. Puis nous faisons pointer cette variable vers la méthode
TriAscendant().
Nul besoin ici d’utiliser les parenthèses, mais juste le nom de la méthode. Il s’agit seulement d’une affectation.
Nous invoquons ensuite la méthode
TriAscendant() à travers la variable qui va permettre de trier le tableau par ordre croissant avant d’afficher son contenu. Cette fois-ci, il faut bien sûr utiliser les parenthèses car nous invoquons la méthode.
Puis nous faisons pointer la variable vers la méthode
TriDescendant() qui va nous permettre de faire la même chose mais avec un tri décroissant.
Nous pouvons appeler cette classe de cette façon :
Code : C# | static void Main(string[] args)
{
int[] tableau = new int[] { 4, 1, 6, 10, 8, 5 };
new TrieurDeTableau().DemoTri(tableau);
}
|
Notre code affichera au final les entiers triés par ordre croissant, puis les mêmes entiers triés par ordre décroissant.
Ok, mais pourquoi utiliser ce délégué ? On pourrait très bien appeler d’abord la méthode de tri ascendant et ensuite la méthode de tri descendant. Comme on l’a toujours fait !
Eh bien, l’intérêt ici est que le délégué est très souple et va permettre de réorganiser le code (on parle également de
refactoriser du code). Ainsi, en rajoutant la méthode suivante dans la classe :
Code : C# | private void TrierEtAfficher(int[] tableau, DelegateTri methodeDeTri)
{
methodeDeTri(tableau);
foreach (int i in tableau)
{
Console.WriteLine(i);
}
}
|
Nous pourrons grandement simplifier la méthode
DemoTri :
Code : C# | public void DemoTri(int[] tableau)
{
TrierEtAfficher(tableau, TriAscendant);
Console.WriteLine();
TrierEtAfficher(tableau, TriDescendant);
}
|
Ce qui produira le même résultat que précédemment. Qu’avons-nous fait ici ?
Nous avons utilisé le délégué comme paramètre d’une méthode. Ce délégué est ensuite utilisé pour invoquer une méthode que nous aurons passée en paramètres. C’est ce que nous faisons en disant d’utiliser la méthode
TrierEtAfficher une première fois avec la méthode
TriAscendant() et une deuxième fois avec la méthode
TriDescendant().
Plutôt pas mal non ?
Il est même possible de définir la méthode qui sera utilisée à l’intérieur de
TrierEtAfficher() sans avoir à l’écrire complètement dans le corps de la classe.
Cela peut être utile si la méthode est vouée à n'être utilisée que dans cette unique situation et qu’elle n’est jamais appelée à un autre endroit.
Par exemple, plutôt que de définir complètement la méthode
TriAscendant(), je pourrais la définir directement au moment de l’appel de la méthode :
Code : C# 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 | public class TrieurDeTableau
{
private delegate void DelegateTri(int[] tableau);
private void TrierEtAfficher(int[] tableau, DelegateTri methodeDeTri)
{
methodeDeTri(tableau);
foreach (int i in tableau)
{
Console.WriteLine(i);
}
}
public void DemoTri(int[] tableau)
{
TrierEtAfficher(tableau, delegate(int[] leTableau)
{
Array.Sort(leTableau);
});
Console.WriteLine();
TrierEtAfficher(tableau, delegate(int[] leTableau)
{
Array.Sort(leTableau);
Array.Reverse(leTableau);
});
}
}
|
Ainsi, je n’aurai plus besoin de la méthode
TriAscendant() ni de la méthode
TriDescendant().
Le fait de définir la méthode directement au niveau du paramètre d’appel est ce qu’on appelle «
utiliser une méthode anonyme ». Anonyme car la méthode n’a pas de nom. Elle n’a de vie qu’à cet endroit-là.
La syntaxe est un peu particulière, mais au lieu d’utiliser une variable de type
delegate qui pointe vers une méthode, c’est comme si on écrivait directement la méthode.
On utilise le mot-clé
delegate suivi de la déclaration du paramètre. Évidemment, le délégué anonyme doit respecter la signature de
DelegateTri que nous avons définie plus haut. Enfin, nous faisons suivre avec un bloc de code qui correspond au corps de la méthode anonyme.
À noter que le fait d’utiliser le mot-clé delegate revient en fait à créer une classe qui dérive de System.Delegate et qui implémente la logique de base d’un délégué. Le C# nous masque tout ceci afin d’être au maximum efficace.