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
Bien sûr, vous pouvez appeler la méthode directement sur l'objet mais envisagez les scénarios suivants:
Select
.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 ditesButton
ne savait pas ce que vous veut que l'action soit lorsque l'utilisateur clique dessusCela 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.
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.
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.
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.
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;
}
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 .