Délégation

La délégation est quelque chose fait régulièrement. Le principe s’applique entre un délégateur et un délégué. Le délégateur délégue des tâches a des délégués.

En programmation, ceci prend la forme d’une fonction qui appel une autre fonction. Une fonction délégue donc une partie de sa tâche. En autre mots, un délégué est une fonction utilisée par une autre fonction.

Par exemple, imaginons un restaurant et le chef est en charge de gérer la cuisine. Le chef va déléguer des tâches à ses sous-chefs, afin de compléter sa propre tâche de gestion de cuisine.

Un autre exemple : imaginons une fonction CalculerTaxes(...). Cette fonction a pour but de retourner le montant en taxe à payer sur un montant pris en paramètre. Cette fonction peut faire appel à une fonction CalculerTPS(...) et une fonction CalculerTVQ(...), afin d’accomplir sa tâche.

Un autre exemple : imaginons une fonction CalculerSomme(...) qui prend en paramètre un tableau d’entiers. Cette fonction a la tâche de retourner la somme de tous les entiers entre 3 et 5 (inclusivement) dans le tableau reçu. Une fonction EstEntre3Et5(...) est appelée pour tester les entiers.

D’autres exemples de délégation :

Fonction qui prend en paramètre une fonction

Contexte

Supposons que nous cherchons une version de CalculerSomme(...) qui additonne seulement les entiers divisible par 3. Une nouvelle fonction EstDivisiblePar3(...) qui prend en paramètre un entier peut être créée pour être utilisée comme délégué.

Problème

Notez un problème majeur avec cette approche : de la redondance !

Pour chaque nouvelle condition utilisée pour filtrer les entiers, il faut créer une nouvelle fonction quasi identique.

Par exemple, si nous voulons seulement additionner les entiers qui sont premiers, il faudra créer une nouvelle fonction CalculerSomme(...) où que le délégué choisi est changé.

Solution

Une fonction peut prendre d’autre fonction en paramètre de plusieurs façon en C#. Premièrement, le type Func<T> peut être utilisé pour créer des références à des fonctions. Le T est pour représenter le type de retour de la fonction référée.

Il existe aussi Func<T, T2, ..., TResult> qui représente une fonction qui prend en paramètre un T, T2, etc. et retourne un TResult.

La solution au problème initial est simple : envoyer le délégué à utiliser en paramètre.

Prédicat

Un prédicat est une fonction qui prend un paramètre et test le paramètre contre des conditions. Le prédicat retourne donc un bool, indiquant si le paramètre passe les tests ou non. Dans la solution plus haut, les délégués envoyés en paramètre à CalculerSomme(...) sont plus précisement des prédicats.

Il est commun de trouver des fonctions qui prennent en paramètre des prédicats.

IEnumerable

Lorsque la bibliothèque LINQ est ajoutée, tous les IEnumerable gagnent des méthodes qui servent à filtrer leurs données.

Les tableaux, List, string et plusieurs autres structures sont énumérables. En fait, foreach fonctionne seulement avec des conteneurs IEnumerable.

La méthode Where(...) par exemple, prend un Func<T, bool>T est le type du contenu dans l’IEnumerable. Cette fonction est bien un prédicat qui va tester un élément T du conteneur et retourner true si l’élément passe les tests.

Voici quelques exemples :

Délégué de sélection

Un délégué peut servir à sélectionner une propriété d’un objet. La méthode Sum(...) d’un IEnumerable sert souvent à retourner la somme des propriétés sélectionnées de tous les éléments du IEnumerable.

Lambda

Écrire une nouvelle fonction pour chaque prédicat ou délégué de sélection est problématique, car il est possible de rapidement polluer une classe avec trop de fonctions.

De plus, il est fréquent que ces fonctions ne seront qu’appelées une seule fois…

Par chance, il est possible d’écrire des fonctions anonymes. Une telle fonction n’as pas de déclaration nommé (ce qui donne une identité à une fonction) et peut être écrite inline. Bref, de manière qu’il est possible de créer des données sans nom de variable attaché, il est possible de créer des fonctions sans nom directement dans une autre fonction.

Les fonctions anonymes prennent souvent la forme d’une lambda, en programmation moderne. Une lambda est une syntaxe qui permet d’écrire une fonction rapidement et sans déclaration.

Passer de syntaxe classique à syntaxe lambdas

Avec paramètres

de

typeRetour NomFonction(type1 param1, type2 param2)
{
    logique
    ...
    return ...; 
}

à

param1, param2, ... => logique

Sans paramètres

de

typeRetour NomFonction()
{
    logique
}

à

() => logique

Exemples

Ici, nous écrivons directement les délégués où nécessaire. Une lambda créer une Func<T...> appropriée, alors elles sont parfaites pour servir de délégués. Aussi, notez qu’il n’est pas nécessaire d’indiquer les types, ou d’écrire return.

Un autre exemple :

Un dernier plus compliqué :