Contact - A propos
Skip Navigation Links.

Les délégués

Descendre !

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:

Résultat du programme

Remonter en haut de la page Descendre !

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:

Résultat du programme

Remonter en haut de la page Descendre !

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:

Résultat du programme

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

Remonter en haut de la page Descendre !

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:

Résultat du programme

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:

Résultat du programme

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

Remonter en haut de la page 

© C-O 2005-2008