DotnetDojo

Développer des applications modernes avec les technologies Microsoft et Open source

  • Blog
  • Vidéos
  • Formations
  • Outils

3 techniques d’extensibilité utilisant les delegates, les delegates multicast et les events en C# (CSharp)

delegate-multicast-event

Dans cet article, je souhaite vous présenter des techniques simples qui permettent de coder des classes extensibles grâce au principe d’ouvert-fermé.

Dans l’article précédent, je vous ai expliqué comment utiliser les delegates en C#. Nous allons désormais utiliser ces delegates pour aller plus loin dans les possibilités proposées par le langage CSharp.

Les premières techniques évoquées

Habituellement, quand je demande aux développeurs (dans mes formations), comment ils mettent en place des mécanismes d’extensibilité, j’ai souvent des réponses qui concernent l’héritage de classes et la surcharge de méthodes.
Ces deux mécanismes sont bien sûr possibles mais ils ne sont pas toujours les plus simples et les plus efficaces à utiliser.

L’utilisation de l’héritage ou de la surcharge va surtout complexifier votre code. Le prochain développeur qui devra lire votre code aura plus de difficultés à comprendre et à réutiliser ce que vous avez écrit.

Au début, j’utilisais aussi l’héritage (un peu trop d’ailleurs :-)) et les surcharges de méthodes. Désormais, j’utilise également d’autres techniques qui permettent de faire les choses autrement et mieux (selon moi).

J’ai parlé du principe dans l’article précédent sur les delegates en csharp. Je vais donc vous résumer rapidement ce qu’il est possible de faire ici puis étendre le mécanisme aux events.

Le principe

Les delegates sont des références vers des méthodes en C#. L’idée est d’utiliser ces références comme mécanisme d’extensibilité.

Le fait de proposer un mécanisme d’extensibilité dans votre code permettra de faire évoluer un comportement existant sans modifier le code qui réalise ce comportement.
L’idée derrière tout ça est simple : une fois un morceau de code testé et validé, il faut essayer de ne plus y toucher.
Pour ajouter un nouveau comportement, il sera possible d’utiliser les delegates en C# ou bien les events.

L’extensibilité grâce aux delegates en CSharp

Les delegates permettent d’avoir des références vers des méthodes « externes » qui pourront être écrites plus tard.
Ainsi, si vous écrivez une méthode, vous pourrez appeler des delegates pour effectuer des opérations dans votre code.

L’utilisation la plus courante des delegates est de pouvoir choisir entre un algorithme et un autre (design pattern Strategy).

Je vous propose de découvrir un exemple qui vous permettra de bien comprendre. Au début, je commence par écrire un algorithme basique (dans l’exemple, il ajoute un nombre aléatoire à chaque élément d’un tableau d’entiers).

[csharp] void Algorithme(int[] nombres)
{
Random rnd = new Random();

for (int n = 0; n < nombres.Length; n++)
{
nombres[n] = nombres[n] + rnd.Next(0, 1);
}
}
[/csharp]

En ajoutant un delegate comme paramètre, je peux proposer un comportement qui permet d’ajouter un filtre paramétrable (ce qui rejoint le design pattern Stratégie).

Voici le code correspondant :

[csharp] delegate int[] FiltreDelegate(int[] nombres);

void Algorithme(int[] nombres, FiltreDelegate filtre)
{
Random rnd = new Random();

// Point d’extensibilité
if (filtre != null) nombres = filtre(nombres);

for (int n = 0; n < nombres.Length; n++)
{
nombres[n] = nombres[n] + rnd.Next(0, 1);
}
}
[/csharp]

Vous remarquerez donc que, grâce au delegate en paramètre, le développeur pourra utiliser son algorithme de différentes façons.
Ce code respecte donc le principe de l’ouvert-fermé : ouvert aux évolutions mais fermé aux modifications.

L’extensibilité grâce aux delegates multicast en CSharp

Les delegates multicast sont un type particulier de delegates. En fait, il est possible d’affecter plusieurs référence de méthode vers un seul delegate.
Cela vous permettra donc d’appeler plusieurs méthodes lorsque vous appelez votre delegate.
Ce qui est sympa c’est qu’un delegate classique peut aussi être utilisé comme delegate multicast sans modifier le code de votre algorithme.

Pour ajouter plusieurs méthodes à un delegate, il suffit d’utiliser l’opérateur « + ». Pour supprimer une méthode, l’opérateur « -« .

Je vous propose donc d’adapter le code précédent pour pouvoir utiliser plusieurs filtres sur les nombres en entrée. Il faudra simplement faire attention à l’ordre des appels (l’ordre d’ajout des delegates).

Voici donc le code adapté :

[csharp] class Program
{
static void Main(string[] args)
{
int[] entree = new int[] { 1, 2, 3 };
Program p = new Program();

FiltreDelegate dlg = new FiltreDelegate(p.SimpleFilter);
// Ajout du second delegate
dlg += p.SecondFilter;

int[] resultat = p.Algorithme(entree, dlg);
Console.Read();
}

void SimpleFilter(ref int[] nombres)
{
List resultat = new List(nombres);
resultat.Remove(1);
nombres = resultat.ToArray();
}

void SecondFilter(ref int[] nombres)
{
List copie = new List(nombres);
copie.Reverse();
nombres = copie.ToArray();
}

delegate void FiltreDelegate(ref int[] nombres);

int[] Algorithme(int[] nombres, FiltreDelegate filtre)
{
Random rnd = new Random();

// Point d’extensibilité
if (filtre != null) filtre(ref nombres);

for (int n = 0; n < nombres.Length; n++)
{
nombres[n] = nombres[n] + rnd.Next(0, 5);
}

return nombres;
}
}
[/csharp]

Il y a une petite subtilité donc je dois vous parler : j’ai utilisé le mot clé « ref » dans le delegate.
Ceci est nécessaire pour que chaque delegate puisse « travailler » sur le même ensemble de données.

Ce mécanisme permet donc d’appeler la méthode Algorithme avec deux filtres :

  • Le premier filtre SimpleFilter supprimer les éléments dont la valeur est égale à 1,
  • Le second filtre SecondFilter inverse l’ordre des éléments.

L’extensibilité avec les événements

Enfin, la troisième méthode dont je voulais vous parler utilise les événements en C# (mot clé event).

Les événements en C# permettent de mettre en place des mécanismes de « rappel » au niveau de la classe. Les classes que vous écrivez peuvent avertir des abonnés que quelque chose c’est passé.
event est un mot clé qui permet de déclarer une propriété du type delegate multicast (cela permet aussi aux events d’être utilisés les interfaces).

Dans le framework .NET, les événements sont utilisés un peu partout (en particulier dans les contrôles graphiques).
Par exemple, sur la classe Button, vous disposez d’un événement « Click ». Celui-ci sera lancé lorsqu’un utilisateur clique sur un bouton.
Ce mécanisme permettra d’appeler plusieurs méthodes pour faire ensuite un traitement.

Il y a une petite subtilité avec les événements : seul la classe qui déclare les events peut lancer ses événements.
Il faudra donc écrire du code dans votre classe afin de lancer les événements appropriés.

Voici une adaptation de l’exemple d’algorithme précédent (j’ai mis l’ensemble dans une classe après un peu de refactoring).

[csharp] // classe Algorithme :
public class Algorithme
{
public delegate void FiltreDelegate(ref int[] nombres);

public event FiltreDelegate AvantCalcul;
public event FiltreDelegate ApresCalcul;

public int[] Calculer(int[] nombres)
{
Random rnd = new Random();

// Point d’extensibilité
// permet de modifier les données avant le calcul
if (AvantCalcul != null) AvantCalcul(ref nombres);

for (int n = 0; n < nombres.Length; n++)
{
nombres[n] = nombres[n] + rnd.Next(0, 2);
}

// Point d’extensibilité
// permet de modifier les données après le calcul
if (ApresCalcul != null) ApresCalcul(ref nombres);

return nombres;
}
}

// classe Program (point d’entrée de l’application console)
class Program
{
static void Main(string[] args)
{
int[] entree = new int[] { 1, 2, 3, 4, 5 };
Algorithme algo = new Algorithme();
Program p = new Program();

// deux filtre avant le calcul
algo.AvantCalcul += p.SimpleFilter;
algo.AvantCalcul += p.SecondFilter;

// un filtre après
algo.ApresCalcul += p.SecondFilter;

int[] resultat = algo.Calculer(entree);
Console.WriteLine(String.Join(" ", resultat));
}

void SimpleFilter(ref int[] nombres)
{
List resultat = new List(nombres);
resultat.Remove(1);
nombres = resultat.ToArray();
}

void SecondFilter(ref int[] nombres)
{
List copie = new List(nombres);
copie.Reverse();
nombres = copie.ToArray();
}
}
[/csharp]

On remarque donc que les événements permettent de mettre en place des mécanismes d’extension au niveau de la classe alors que les delegate sont utilisés pour étendre des méthodes.

Le plan d’action (en bonus)

Si vous souhaitez utiliser ces techniques, je vous ai préparé un plan d’action que vous pouvez utiliser lorsque vous écrivez de nouvelles classes. Je vous explique également comment choisir entre l’héritage, la surcharge de méthodes, les delegates et les events.

[sociallocker] Télécharger le bonus complémentaire
[/sociallocker]

Besoin de résultats rapides ?

Découvrez les formations vidéos que je propose :

  

 

Formations en présentiel (dans toute la France)

Découvrez également les formations C# et .NET que je donne en présentiel (en France)

A propos de DotnetDojo

Pascal Lacroix

Je m’appelle Pascal et je suis passionné par le développement logiciel, l’efficacité et l’entrepreneuriat. Sur DotnetDojo, je vous propose des méthodes pour apprendre à développer des applications modernes avec les technologies Microsoft et Open Source.

En savoir plus

Liens complémentaires

  • A propos de DotnetDojo
  • 18 principes pour professionnaliser le développement logiciel
  • Boite à outils du développeur
  • Tous les articles
  • Liste des formations
  • Contact

Copyright 2019 Jupiteo · Mentions légales · Contact · CGV · Offres d'emploi .NET · Formations Dotnet