Les délégués
Définition
Exemples
La classe System.Delegate
La propriété publique Method
La méthode publique DynamicInvoke
La méthode publique GetHashCode
Encore plus simple avec C# 2
La classe MulticastDelegate
Définition
Le langage C# permet de créer des classes particulières que l'on nomme délégués. Pour être plus précis, la déclaration d'un délégué définit une classe dérivée de la classe
System.Delegate. Autrement dit, un délégué est de type classe. La chose à retenir est qu'un
délégué représente une référence vers une ou plusieurs méthodes. Ces méthodes peuvent être statiques ou non. Le mot-clé delegate permet de déclarer un délégué.
Exemples
Notre premier exemple est assez simple. Le code suivant est loin de mettre en avant toute la puissance des délégués mais il permet de bien comprendre le concept de base.
Commençons par nous intéresser à ce que nous connaissons déjà, c'est-à-dire toute la partie du code colorée en bleu.
Nous déclarons une classe MaClasse qui définit un champ x public de type entier ainsi qu'une méthode Incrementer publique qui renvoie un entier et qui
prend un entier y en paramètre. Par défaut, x vaut 0. Nous déclarons également une classe Principale qui contient le point d'entrée. Dans cette classe nous créons
une instance de MaClasse nommée mc.
La première ligne verte déclare un délégué Delegue qui renvoie un entier et qui possède un paramètre y de type entier.
Pour indiquer qu'un délégué D référence une méthode M, M sera placée en paramètre d'une instance de D.
Pour des raisons de compatibilité, il est indispensable de s'assurer que:
-
D et M aient le même type de retour.
-
D et M aient le même nombre de paramètres, avec les mêmes types et dans le même ordre.
Une méthode compatible avec Delegue doit donc renvoyer un entier et accepter un entier en paramètre. La méthode Incrementer répond parfaitement à ces critères.
Delegue peut donc référencer Incrementer. C'est ce que fait la deuxième ligne verte: nous créons une instance D de Delegue qui référence la méthode
Incrementer.
Une instance de délégué peut faire référence:
-
soit à une méthode statique.
-
soit à un objet et à une méthode non statique.
-
soit à un autre délégué.
Incrementer n'est pas statique. C'est pourquoi nous avons créé l'objet mc auquel nous appliquons Incrementer. Parmi les trois cas précédemment cités, nous sommes
dans le second cas.
Une instance de délégué appelle une méthode en utilisant les arguments qu'elle a reçus.
À la troisième ligne verte, la valeur 4 passée à D représente l'argument de la méthode Incrementer.
using System;
delegate int Delegue(int x);
class MaClasse
{
public int x = 0;
public int Incrementer(int y)
{
x += y;
return x;
}
}
class Principale
{
public static void Main()
{
MaClasse mc = new MaClasse();
Delegue D = new Delegue(mc.Incrementer);
Console.WriteLine("Avant incrementation: mc.x = " + mc.x);
D(4);
Console.WriteLine("Apres incrementation de 4: mc.x = " + mc.x);
}
}
Voici le résultat du programme:
Notre second exemple, qui est encore plus simple, illustre le cas des méthodes statiques. Le programme suivant contient une unique classe Principale dans laquelle nous déclarons
un délégué Delegue de type void qui possède un paramètre de type string et une méthode statique Coucou adaptée à Delegue.
Le point d'entrée se contente de créer une instance D de Delegue auquel nous passons une string en paramètre.
using System;
public class Principale
{
public delegate void Delegue(string s);
public static void Coucou(string s)
{
Console.WriteLine(s);
}
public static void Main()
{
Delegue D = new Delegue(Coucou);
D("Coucou !");
}
}
Le résultat de ce programme n'est guère surprenant: Coucou !
Notre dernier exemple est bien plus intéressant dans la mesure où il montre en quoi les délégués sont très utiles. Pour cette illustration, nous reprenons le code du premier exemple auquel nous rajoutons un constructeur
MaClasse et une méthode Multiplier. Le constructeur de MaClasse permet d'initialiser le champ
x et la méthode Multiplier permet de multiplier x par une valeur y qui représente le paramètre de Multiplier.
Nous créons trois instances, mc2, mc7 et mc18, de MaClasse. Nous déclarons ensuite une instance D de Delegue qui
référence la méthode Multiplier pour l'objet mc2.
Nous pouvons donc multiplier mc2.x par un entier k quelconque. Mais comment faire si nous souhaitons multiplier par k le membre x de plusieurs instances de
MaClasse ? Une solution consisterait à créer une instance de Delegue pour chaque instance de MaClasse dont on souhaite multiplier le champ x. Cette
solution semble un peu lourde. En fait, il existe un moyen de réaliser cette tâche en utilisant une unique instance de Delegue.
Une unique instance de délégué permet de référencer une ou plusieurs méthodes.
On nomme liste d'appel l'ensemble des méthodes référencées par une instance de délégué.
Cependant, il ne faut pas oublier de respecter les règles de compatibilité entre délégués et méthodes.
Nous avons donc réalisé un premier référencement avec la méthode Multiplier appliquée à mc2.x.
L'opérateur += permet de rajouter une référence à un délégué.
Nous utilisons cet opérateur afin d'indiquer que D référence aussi la méthode Multiplier appliquée à l'objet mc18.
Jusqu'ici, nous avons créé deux références avec la même méthode (Multiplier) appliquée à des objets différents. Mais D peut aussi référencer d'autres méthodes comme nous le
faisons avec la méthode Incrémenter appliquée à l'objet mc7. Attention, nous ne souhaitons qu'incrémenter mc7.x sans modifier le membre x
des deux autres objets. Pour cela nous devons supprimer les références vers mc2.Multiplier et mc18.Multiplier en utilisant l'opérateur -=. Sans supprimer ces références, nous
aurions aussi modifié mc2.x ( = 80) et mc18.x ( = 720).
using System;
delegate int Delegue(int x);
class MaClasse
{
public int x = 0;
public MaClasse(int y)
{
x = y;
}
public int Multiplier(int y)
{
x *= y;
return x;
}
public int Incrementer(int y)
{
x += y;
return x;
}
}
class Principale
{
public static void Main()
{
MaClasse mc2 = new MaClasse(2);
MaClasse mc7 = new MaClasse(7);
MaClasse mc18 = new MaClasse(18);
Console.WriteLine("Avant la modification des valeurs");
Console.Write("mc2.x = {0}\tmc7.x = {1}\t", mc2.x, mc7.x);
Console.Write("mc18.x = {0}\n", mc18.x);
Delegue D = new Delegue(mc2.Multiplier);
D += new Delegue(mc18.Multiplier);
D(5);
Console.WriteLine("\nApres avoir multiplie mc2.x et mc18.x par 5");
Console.Write("mc2.x = {0}\tmc7.x = {1}\t", mc2.x, mc7.x);
Console.Write("mc18.x = {0}\n", mc18.x);
D += new Delegue(mc7.Incrementer);
D -= new Delegue(mc2.Multiplier);
D -= new Delegue(mc18.Multiplier);
D(8);
Console.Write("\nApres avoir incrementeu mc7.x de 8\n");
Console.Write("mc2.x = {0}\tmc7.x = {1}\t", mc2.x, mc7.x);
Console.Write("mc18.x = {0}\n", mc18.x);
}
}
Résultat du programme:
La classe System.Delegate
Comme nous le savons déjà, la classe System.Delegate représente
un délégué. Nous testons ici plusieurs membres de cette classe.
La propriété publique Method
La propriété publique Method permet d'obtenir la méthode représentée par un délégué. Pour tester cette propriété, nous pouvons reprendre le dernier programme du paragraphe précédent en plaçant la ligne:
Console.WriteLine("Methode referencee par D: " + D.Method);
après chaque ligne verte.
Voici ce que nous obtenons:
Ce résultat montre que la propriété Method permet d'obtenir la dernière méthode référencée par un délégué.
Je souhaite en savoir plus sur la propriété Method
La méthode publique DynamicInvoke
La méthode publique DynamicInvoke permet d'appeler dynamiquement la dernière méthode référencée par un délégué. DynamicInvoke prend un tableau d'objets en paramètre.
Dans l'exemple suivant, nous déclarons un délégué Delegue de type entier, qui prend trois entiers en paramètres. La classe MaClasse
définit une méthode Additionner adaptée au délégué Delegue. Additionner réalise l'addition de trois entiers et du champ x. Dans Main,
nous déclarons un tableau d'objets dont les éléments correspondent aux paramètres de la méthode référencée par D, c'est-à-dire Additionner.
using System;
public delegate int Delegue(int a, int b, int c);
class MaClasse
{
public int x = 0;
public MaClasse(int y)
{
x = y;
}
public int Additionner(int a, int b, int c)
{
x = x + a + b + c;
return x;
}
}
class Principale
{
public static void Main()
{
MaClasse mc = new MaClasse(112);
Console.Write("Avant la modification de mc.x\n");
Console.WriteLine("mc.x = {0}", mc.x);
Delegue D = new Delegue(mc.Additionner);
Object[] obj = {1,2,3};
D.DynamicInvoke(obj);
Console.WriteLine("mc.x + 6 = {0}", mc.x);
}
}
Résultat:
Je souhaite en savoir plus sur la méthode
DynamicInvoke
La méthode publique GetHashCode
La méthode publique GetHashCode permet d'obtenir le code de hachage d'un délégué.
using System;
public delegate void vDelegue(string s);
public delegate double iDelegue(int x);
public class Principale
{
public static void Coucou(string s)
{
Console.WriteLine(s);
}
public static double Racine(int x)
{
return Math.Pow(x, 0.5);
}
public static void Main()
{
vDelegue vD = new vDelegue(Coucou);
iDelegue iD = new iDelegue(Racine);
vD("vD dit: coucou !");
double res = iD(8);
Console.WriteLine("Racine(8) = " + res);
int hcodeD1 = vD.GetHashCode();
int hcodeD2 = iD.GetHashCode();
Console.WriteLine("Code de hachage de vD: " + hcodeD1);
Console.WriteLine("Code de hachage de iD: " + hcodeD2);
}
}
Résultat:
Je souhaite en savoir plus sur la méthode
GetHashCode
Découvrez les autres membres de la classe
System.Delegate.
Consultez les MSDN pour en savoir plus sur les délégués.
Mercredi 6 Juillet 2005
Encore plus simple avec C# 2
Comme nous le savons déjà, un délégué est un type classe qui permet de référencer
plusieurs méthode.
Voici un petit exemple qui nous rappelle la syntaxe que nous utilisions dans la première
version de C#:
namespace UnEspaceDeNom
{
class Program
{
delegate int UnDelegue();
delegate void UnAutreDelegue(string s);
public int Retourner1()
{
int x = 1;
Console.WriteLine("Je suis référencée par le délégué numéro " + x);
return x;
}
public int Retourner1Bis()
{
int x = 1;
Console.WriteLine("Je suis également référencée par le délégué numéro " + x);
return x;
}
static void AfficherChaine(string s)
{
Console.WriteLine("On m'a passé la chaîne: \"" + s + "\"");
}
static void Main(string[] args)
{
Program monProg = new Program();
UnDelegue ud = new UnDelegue(monProg.Retourner1);
ud += monProg.Retourner1Bis;
ud();
UnAutreDelegue uad = new UnAutreDelegue(AfficherChaine);
uad("On va faire mieux avec C# 2 !");
}
}
}
Bien entendu, nous pouvons toujours utiliser cette syntaxe dans la deuxième version
du langage. Cependant, C# 2 introduit une petite facilité d'écriture en ce qui concerne le
référencement des méthodes. Dans le programme précédent, les changements ont
uniquement lieu dans la méthode Main:
static void Main()
{
Program monProg = new Program();
UnDelegue ud = monProg.Retourner1;
ud += monProg.Retourner1Bis;
ud();
UnAutreDelegue uad = AfficherChaine;
uad("On va faire mieux avec C# 2 !");
}
On obtient donc un code un peu plus lisible mais cela ne change rien en interne
! Pour s'en
convaincre, il suffit de jeter un petit coup d'oeil dans le code MSIL
de l'assemblage
du programme. Dans les deux cas, nous
avons le code suivant:
.method private hidebysig static void Main(string[] args) cil managed
{
...
IL_0006: ldloc.0
IL_0007: ldftn instance int32 UnEspaceDeNom.Program::Retourner1()
IL_000d: newobj instance void UnEspaceDeNom.Program/UnDelegue::.ctor(object, native int) ...
IL_0014: ldloc.0
IL_0015: ldftn instance int32 UnEspaceDeNom.Program::Retourner1Bis()
IL_001b: newobj instance void UnEspaceDeNom.Program/UnDelegue::.ctor(object, native int)
...
IL_0032: ldnull
IL_0033: ldftn void UnEspaceDeNom.Program::AfficherChaine(string)
IL_0039: newobj instance void UnEspaceDeNom.Program/UnAutreDelegue::.ctor(object, native int)
... } // end of method Program::Main
Il y a bien appel aux constructeurs de nos types délégués
UnDelegue et UnAutreDelegue
(en rouge).
La classe MulticastDelegate
La classe
MulticastDelegate (qui existait déjà avant C# 2) représente un délégué dont
la liste d'appel peut contenir plus d'un élément. Vous trouverez toutes les informations
concernant cette classe (très spéciale) sur les MSDN.
Dimanche 19 Février 2006
|