Comparaison et Utilisation des Délégués en C# : Delegate, Action, Func et Predicate

Introduction aux Délégués

Un délégué est un type de référence qui encapsule une méthode avec une signature spécifique. Il permet de traiter les méthodes comme des objets, de les stocker dans des variables et de les passer en paramètres. Les événements, quant à eux, sont une implémentation spécifique et sécurisée basée sur les délégués multicast.

  1. Déclaration des Délégués

1.1 Le mot-clé delegate

La déclaration classique permet de définir une signature personnalisée. Elle accepte jusqu'à 32 paramètres et peut retourner n'importe quel type, y compris void.

// Signature personnalisée pour le calcul de surface
public delegate double SurfaceCalculator(double width, double height);

1.2 Le délégué Action

Action est un délégué générique intégré qui ne retourne aucune valeur (void). Il peut accepter de 0 à 16 paramètres d'entrée.

// Exemple d'utilisation avec un paramètre générique
public void ExecuteTask<T>(Action<T> task, T payload)
{
    task(payload);
}

1.3 Le délégué Func

Func est conçu pour les opérations qui renvoient un résultat. Le dernier argument générique représente toujours le type de retour. Il supporte jusqu'à 16 paramètres d'entrée.

// Transformation de données avec retour
public TResult ApplyTransformation<TInput, TResult>(Func<TInput, TResult> transformer, TInput data)
{
    return transformer(data);
}

1.4 Le délégué Predicate

Spécialisé dans l'évaluation de conditions, Predicate<T> prend exactement un paramètre d'entrée et retourne toujours un booléen.

// Signature équivalente
public delegate bool ConditionEvaluator<T>(T item);
  1. Mise en Pratique

2.1 Utilisation de delegate

public delegate double MathOperation(double a, double b);

class Program
{
    static void Main()
    {
        MathOperation op = new MathOperation(Multiply);
        Console.WriteLine($"Résultat : {op(5.5, 2.0)}");
    }

    static double Multiply(double x, double y) => x * y;
}

2.2 Utilisation de Action

Idéal pour les opérations de type "fire-and-forget", le traitement de données ou les callbacks sans retour.

static void Main()
{
    ProcessUser("Alice", user => Console.WriteLine($"Utilisateur connecté : {user}"));
    ProcessUser(42, id => Console.WriteLine($"ID traité : {id}"));
}

public static void ProcessUser<T>(Action<T> handler, T data)
{
    handler(data);
}

2.3 Utilisation de Func

static void Main()
{
    double finalPrice = CalculateDiscount(150.0, price => price * 0.85);
    Console.WriteLine($"Prix final : {finalPrice}");
}

public static double CalculateDiscount(Func<double, double> discountLogic, double initialPrice)
{
    return discountLogic(initialPrice);
}

2.4 Utilisation de Predicate

Très utile pour le filtrage de collections, notamment avec les méthodes de recherche intégrées de la classe Array ou List<T>.

struct Transaction
{
    public string Id;
    public double Amount;
}

static void Main()
{
    Transaction[] ledger = {
        new Transaction { Id = "T1", Amount = 120.50 },
        new Transaction { Id = "T2", Amount = 850.00 },
        new Transaction { Id = "T3", Amount = 45.00 }
    };

    // Recherche de la première transaction supérieure à 500
    Transaction highValue = Array.Find(ledger, IsHighValue);
    Console.WriteLine($"Trouvé : {highValue.Id} pour {highValue.Amount}");
}

static bool IsHighValue(Transaction t) => t.Amount > 500.0;
  1. Nettoyage et Gestion de la Mémoire

Les délégués multicast peuvent conserver des références fortes à des objets, empêchant le garbage collector de les récupérer. Il est crucial de savoir les nettoyer pour éviter les fuites de mémoire.

3.1 Réinitialisation directe

La méthode la plus simple et la plus radicale pour vider complètement une chaîne de délégués est de la réassigner à null.

public MathOperation OnOperation;

public void ClearAllOperations()
{
    OnOperation = null;
}

3.2 Suppression ciblée via GetInvocationList

Si vous devez retirer des abonnés spécifiques d'un événement ou d'un délégué multicast sans perdre les autres, parcourez la liste d'invocation.

public MathOperation OnOperation;

public void UnsubscribeAll()
{
    if (OnOperation != null)
    {
        Delegate[] subscribers = OnOperation.GetInvocationList();
        foreach (MathOperation subscriber in subscribers)
        {
            OnOperation -= subscriber;
        }
    }
}
  1. Caractéristiques Fondamentales

  • Sécurité de type : Contrairement aux pointeurs de fonction en C++, les délégués C# sont strictement vérifiés par le compilateur au moment de la compilation.
  • Injection de comportement : Ils permettent de découpler la logique métier en passant des algorithmes dynamiques en paramètres.
  • Multicast : Un seul délégué peut invoquer une chaîne de plusieurs méthodes séquentielelment via les opérateurs + et -.
  • Covariance et Contravariance : La signature de la méthode cible n'a pas besoin de correspondre exactement à celle du délégué, offrant une grande flexibilité grâce aux conversions implicites de types de référence.

Étiquettes: C# delegates Action Func Predicate

Publié le 3 juillet à 22h15