web-dev-qa-db-fra.com

Pourquoi avons-nous besoin de délégués C #

Je ne semble jamais comprendre pourquoi nous avons besoin de délégués? Je sais que ce sont des types de référence immuables qui détiennent la référence d'une méthode, mais pourquoi ne pouvons-nous pas simplement appeler la méthode directement, au lieu de l'appeler via un délégué?

Merci

56
InfoLearner

Bien sûr, vous pouvez appeler la méthode directement sur l'objet mais envisagez les scénarios suivants:

  1. Vous voulez appeler une série de méthodes en utilisant un seul délégué sans écrire beaucoup d'appels de méthodes.
  2. Vous souhaitez implémenter un système basé sur des événements avec élégance.
  3. Vous souhaitez appeler deux méthodes de la même façon dans la signature, mais résidez dans des classes différentes.
  4. Vous voulez passer la méthode comme paramètre.
  5. Vous ne voulez pas écrire beaucoup de code polymorphe comme dans LINQ, vous pouvez fournir beaucoup d'implémentation à la méthode Select.
33
TalentTuner

Réponse simple: le code devant exécuter l'action ne connaît pas la méthode à appeler lorsqu'elle est écrite. Vous ne pouvez appeler la méthode directement que si vous savez au moment de la compilation quelle méthode appeler, non? Donc, si vous voulez résumer l'idée de "réaliser l'action X au moment approprié", vous avez besoin d'une représentation de l'action, de sorte que la méthode appelant l'action n'ait pas besoin de connaître l'implémentation exacte à l'avance.

Par exemple:

  • Enumerable.Select dans LINQ ne peut pas connaître la projection que vous souhaitez utiliser sauf si vous le lui dites
  • L'auteur de Button ne savait pas ce que vous veut que l'action soit lorsque l'utilisateur clique dessus
  • Si un nouveau fil ne faisait qu'une seule chose, ce serait assez ennuyeux ...

Cela peut vous aider à penser que les délégués sont comme des interfaces à méthode unique, mais avec beaucoup de syntaxe de langage pour les rendre faciles à utiliser et un support génial pour l'exécution asynchrone et la multidiffusion.

59
Jon Skeet

Parce que vous ne pouvez pas encore avoir la méthode écrite, ou que vous avez conçu votre classe de telle manière qu'un utilisateur de celle-ci puisse décider quelle méthode (cet utilisateur a écrit) l'utilisateur veut que votre classe s'exécute.

Ils rendent également certaines conceptions plus propres (par exemple, au lieu d'une instruction switch où vous appelez différentes méthodes, vous appelez le délégué transmis) et plus faciles à comprendre et permettent d'étendre votre code sans le changer (pensez OCP).

Les délégués sont également la base du système de gestion des événements - écrire et enregistrer des gestionnaires d'événements sans délégués serait beaucoup plus difficile qu'avec eux.

Voir les différents délégués Action et Func dans Linq - ce ne serait guère aussi utile sans eux.

Cela dit, personne ne vous oblige à utiliser des délégués.

17
Oded
  1. Les délégués soutiennent les événements
  2. Les délégués donnent à votre programme un moyen d'exécuter des méthodes sans avoir à savoir précisément quelles sont ces méthodes au moment de la compilation
8
Nanda

Tout ce qui peut être fait avec des délégués peut être fait sans eux, mais les délégués fournissent une manière beaucoup plus propre de les faire. Si l'on n'avait pas de délégués, il faudrait définir une interface ou une classe de base abstraite pour chaque signature de fonction possible contenant une fonction Invoke (paramètres appropriés), et définir une classe pour chaque fonction qui devait être appelable par des pseudo-délégués. Cette classe hériterait de l'interface appropriée pour la signature de la fonction, contiendrait une référence à la classe contenant la fonction qu'elle était censée représenter, et une méthode implémentant Invoke (paramètres appropriés) qui appellerait la fonction appropriée dans la classe à laquelle elle contient une référence. Si la classe Foo a deux méthodes Foo1 et Foo2, prenant toutes deux un seul paramètre, qui peuvent toutes deux être appelées par des pseudo-délégués, il y aurait deux classes supplémentaires créées, une pour chaque méthode.

Sans le support du compilateur pour cette technique, le code source devrait être assez odieux. Si le compilateur pouvait générer automatiquement les classes imbriquées appropriées, les choses pourraient être assez propres. La vitesse d'envoi des pseudo-délégués serait probablement généralement plus lente qu'avec les délégués conventionnels, mais si les pseudo-délégués étaient une interface plutôt qu'une classe de base abstraite, une classe qui n'a besoin que de faire un pseudo-délégué pour une méthode d'une signature donnée pourrait implémenter l'interface pseudo-déléguée appropriée elle-même; l'instance de classe pourrait alors être transmise à tout code attendant un pseudo-délégué de cette signature, évitant ainsi la nécessité de créer un objet supplémentaire. De plus, alors que le nombre de classes dont on aurait besoin lors de l'utilisation de pseudo-délégués serait plus élevé que lors de l'utilisation de "vrais" délégués, chaque pseudo-délégué n'aurait besoin que de contenir une seule instance d'objet.

4
supercat

Pensez aux pointeurs de fonction C/C++, à la façon dont vous traitez les fonctions de gestion des événements javascript comme des "données" et passez-les. Dans le langage Delphi, il existe également un type procédural. Dans les coulisses, les expressions déléguées C # et lambda, et toutes ces choses sont essentiellement la même idée: coder en tant que données. Et cela constitue la base même de la programmation fonctionnelle.

3
xiachengbin

Vous avez demandé un exemple de la raison pour laquelle vous passeriez une fonction en tant que paramètre, j'en ai une parfaite et j'ai pensé que cela pourrait vous aider à comprendre, c'est assez académique mais montre une utilité. Supposons que vous ayez une méthode ListResults () et une méthode getFromCache (). Au lieu de cela, effectuez de nombreuses vérifications si le cache est nul, etc., vous pouvez simplement passer n'importe quelle méthode à getCache, puis l'invoquer uniquement si le cache est vide dans la méthode getFromCache:

_cacher.GetFromCache(delegate { return ListResults(); }, "ListResults");

public IEnumerable<T> GetFromCache(MethodForCache item, string key, int minutesToCache = 5)
    {
        var cache = _cacheProvider.GetCachedItem(key);
        //you could even have a UseCache bool here for central control
        if (cache == null)
        {
            //you could put timings, logging etc. here instead of all over your code
            cache = item.Invoke();
            _cacheProvider.AddCachedItem(cache, key, minutesToCache);
        }            

        return cache;
    }
1
GarethReid

Vous pouvez les considérer comme une construction similaire avec des pointeurs vers des fonctions en C/C++. Mais ils sont plus que cela en C #. Détails .

0
Liviu M.