quand et pourquoi utiliser les delegates?

Je suis relativement nouveau en C #, et je me demande quand utiliser les delegates de manière appropriée . ils sont largement utilisés dans la déclaration des événements, mais quand dois-je les utiliser dans mon propre code et pourquoi sont-ils utiles? pourquoi ne pas utiliser autre chose?

Je me demande aussi quand je dois utiliser des delegates et je n’ai pas d’autre alternative .

Merci pour l’aide!

EDIT: Je pense que j’ai trouvé un usage nécessaire des delegates ici

Je suis d’accord avec tout ce qui se dit déjà, en essayant simplement de dire quelques mots.

Un délégué peut être considéré comme un espace réservé pour une / des méthode (s).

En définissant un délégué, vous dites à l’utilisateur de votre classe: ” N’hésitez pas à atsortingbuer au délégué toute méthode correspondant à cette signature, qui sera appelée à chaque appel de mon délégué “.

L’utilisation typique est bien sûr des événements. Tout le délégué OnEventX aux méthodes définies par l’utilisateur.

Les delegates sont utiles pour offrir à l’ utilisateur de vos objects une certaine capacité à personnaliser leur comportement. La plupart du temps, vous pouvez utiliser d’autres moyens pour atteindre le même objective et je ne pense pas que vous puissiez jamais être obligé de créer des delegates. C’est le moyen le plus simple dans certaines situations de faire avancer les choses.

Un délégué est une référence à une méthode. Alors que les objects peuvent facilement être envoyés en tant que parameters dans des méthodes, des constructeurs ou autres, les méthodes sont un peu plus compliquées. Mais de temps en temps, vous pourriez avoir besoin d’envoyer une méthode en paramètre à une autre méthode, et c’est à ce moment-là que vous aurez besoin de delegates.

using System; using System.Collections.Generic; using System.Linq; using System.Text; using MyLibrary; namespace DelegateApp { ///  /// A class to define a person ///  public class Person { public ssortingng Name { get; set; } public int Age { get; set; } } class Program { //Our delegate public delegate bool FilterDelegate(Person p); static void Main(ssortingng[] args) { //Create 4 Person objects Person p1 = new Person() { Name = "John", Age = 41 }; Person p2 = new Person() { Name = "Jane", Age = 69 }; Person p3 = new Person() { Name = "Jake", Age = 12 }; Person p4 = new Person() { Name = "Jessie", Age = 25 }; //Create a list of Person objects and fill it List people = new List() { p1, p2, p3, p4 }; //Invoke DisplayPeople using appropriate delegate DisplayPeople("Children:", people, IsChild); DisplayPeople("Adults:", people, IsAdult); DisplayPeople("Seniors:", people, IsSenior); Console.Read(); } ///  /// A method to filter out the people you need ///  /// A list of people /// A filter /// A filtered list static void DisplayPeople(ssortingng title, List people, FilterDelegate filter) { Console.WriteLine(title); foreach (Person p in people) { if (filter(p)) { Console.WriteLine("{0}, {1} years old", p.Name, p.Age); } } Console.Write("\n\n"); } //==========FILTERS=================== static bool IsChild(Person p) { return p.Age < 18; } static bool IsAdult(Person p) { return p.Age >= 18; } static bool IsSenior(Person p) { return p.Age >= 65; } } } 

Supposons que vous souhaitiez écrire une procédure pour intégrer une fonction à valeur réelle f ( x ) sur un certain intervalle [a, b]. Disons que nous voulons utiliser la méthode gaussienne à 3 points pour ce faire (tout le monde le fera, bien sûr).

Idéalement, nous voulons une fonction qui ressemble à:

 // 'f' is the integrand we want to integrate over [a, b] with 'n' subintervals. static double Gauss3(Integrand f, double a, double b, int n) { double res = 0; // compute result // ... return res; } 

On peut donc passer n’importe quel Integrand , f et obtenir son intégrale définie sur l’intervalle fermé.

Quel type devrait être Integrand ?

Sans delegates

Eh bien, sans delegates, nous aurions besoin d’une sorte d’interface avec une seule méthode, par exemple eval déclarée comme suit:

 // Interface describing real-valued functions of one variable. interface Integrand { double eval(double x); } 

Il faudrait ensuite créer tout un tas de classes implémentant cette interface, comme suit:

 // Some function class MyFunc1 : Integrand { public double eval(double x) { return /* some_result */ ; } } // Some other function class MyFunc2 : Integrand { public double eval(double x) { return /* some_result */ ; } } // etc 

Pour les utiliser dans notre méthode Gauss3, nous devons l’invoquer comme suit:

 double res1 = Gauss3(new MyFunc1(), -1, 1, 16); double res2 = Gauss3(new MyFunc2(), 0, Math.PI, 16); 

Et Gauss3 doit faire comme suit:

 static double Gauss3(Integrand f, double a, double b, int n) { // Use the integrand passed in: f.eval(x); } 

Nous devons donc tout faire pour utiliser nos fonctions arbitraires dans Guass3 .

Avec des delegates

 public delegate double Integrand(double x); 

Maintenant, nous pouvons définir certaines fonctions statiques (ou non) adhérant à ce prototype:

 class Program { public delegate double Integrand(double x); // Define implementations to above delegate // with similar input and output types static double MyFunc1(double x) { /* ... */ } static double MyFunc2(double x) { /* ... */ } // ... etc ... public static double Gauss3(Integrand f, ...) { // Now just call the function naturally, no f.eval() stuff. double a = f(x); // ... } // Let's use it static void Main() { // Just pass the function in naturally (well, its reference). double res = Gauss3(MyFunc1, a, b, n); double res = Gauss3(MyFunc2, a, b, n); } } 

Pas d’interfaces, pas de trucs maladroits, pas d’instanciation d’object, juste une simple utilisation de pointeur de fonction pour une tâche simple.

Bien sûr, les delegates sont plus que des pointeurs de fonction sous le capot, mais il s’agit d’une question distincte (chaînage des fonctions et événements).

Les delegates sont extrêmement utiles lorsque vous souhaitez déclarer un bloc de code que vous souhaitez faire circuler. Par exemple lorsque vous utilisez un mécanisme de nouvelle tentative générique.

Pseudo:

 function Retry(Delegate func, int numberOfTimes) try { func.Invoke(); } catch { if(numberOfTimes blabla) func.Invoke(); etc. etc. } 

Ou lorsque vous voulez effectuer une évaluation tardive des blocs de code, comme une fonction pour laquelle vous avez une action Transform , et souhaitez avoir une action BeforeTransform et AfterTransform que vous pouvez évaluer dans votre fonction Transform sans avoir à savoir si BeginTransform est rempli , ou ce qu’il doit transformer.

Et bien sûr lors de la création de gestionnaires d’événements. Vous ne voulez pas évaluer le code maintenant, mais uniquement lorsque vous en avez besoin, de sorte que vous enregistrez un délégué pouvant être appelé lorsque l’événement se produit.

Aperçu des delegates

Les delegates ont les propriétés suivantes:

  • Les delegates sont similaires aux pointeurs de fonctions C ++, mais sont de type sécurisé.
  • Les delegates permettent de transmettre des méthodes en tant que parameters.
  • Les delegates peuvent être utilisés pour définir des méthodes de rappel.
  • Les delegates peuvent être enchaînés ensemble; Par exemple, plusieurs méthodes peuvent être appelées sur un même événement.
  • Les méthodes n’ont pas besoin de correspondre exactement à la signature du délégué. Pour plus d’informations, voir Variations de covariance et Contra.
  • C # version 2.0 introduit le concept de méthodes anonymes, qui permettent de transmettre des blocs de code en tant que parameters à la place d’une méthode définie séparément.

Je viens de me pencher là-dessus et je vais vous donner un exemple car vous avez déjà des descriptions, mais pour le moment, je vois l’avantage de contourner les avertissements du style de référence circulaire où vous ne pouvez pas référencer deux projets. autre.

Supposons qu’une application télécharge un fichier XML, puis enregistre le fichier XML dans une firebase database.

J’ai 2 projets ici qui construisent ma solution: FTP et une SaveDatabase.

Ainsi, notre application commence par rechercher tout téléchargement et télécharger le ou les fichiers, puis il appelle le projet SaveDatabase.

Maintenant, notre application doit notifier le site FTP lorsqu’un fichier est enregistré dans la firebase database en téléchargeant un fichier avec des métadonnées (ignorez pourquoi, il s’agit d’une demande du propriétaire du site FTP). Le problème est à quel point et comment? Nous avons besoin d’une nouvelle méthode appelée NotifyFtpComplete () mais dans laquelle de nos projets devrait-il être enregistré aussi – FTP ou SaveDatabase? Logiquement, le code devrait vivre dans notre projet FTP. Mais cela voudrait dire que notre NotifyFtpComplete devra être déclenché ou qu’il devra attendre que la sauvegarde soit terminée, puis interroger la firebase database pour s’assurer qu’elle est bien là. Ce que nous devons faire, c’est dire à notre projet SaveDatabase d’appeler directement la méthode NotifyFtpComplete () mais nous ne le pouvons pas. nous aurions une référence ciruclar et NotifyFtpComplete () est une méthode privée. Quel dommage, cela aurait fonctionné. Eh bien, c’est possible.

Pendant le code de notre application, nous aurions passé des parameters entre les méthodes, mais si l’un de ces parameters était la méthode NotifyFtpComplete. Oui, nous passons la méthode, avec tout le code à l’intérieur aussi. Cela signifierait que nous pourrions exécuter la méthode à tout moment, à partir de n’importe quel projet. Eh bien, c’est ce que le délégué est. Cela signifie que nous pouvons passer la méthode NotifyFtpComplete () en tant que paramètre à notre classe SaveDatabase (). Au moment où il enregistre, il exécute simplement le délégué.

Voir si cet exemple brut aide (pseudo-code). Nous supposerons également que l’application démarre avec la méthode Begin () de la classe FTP.

 class FTP { public void Begin() { ssortingng filePath = DownloadFileFromFtpAndReturnPathName(); SaveDatabase sd = new SaveDatabase(); sd.Begin(filePath, NotifyFtpComplete()); } private void NotifyFtpComplete() { //Code to send file to FTP site } } class SaveDatabase { private void Begin(ssortingng filePath, delegateType NotifyJobComplete()) { SaveToTheDatabase(filePath); //InvokeTheDelegate - here we can execute the NotifyJobComplete method at our preferred moment in the application, despite the method being private and belonging to a different class. NotifyJobComplete.Invoke(); } } 

Donc, avec cela expliqué, nous pouvons le faire pour de vrai avec cette application console en utilisant C #

 using System; namespace ConsoleApplication1 { //I've made this class private to demonstrate that the SaveToDatabase cannot have any knowledge of this Program class. class Program { static void Main(ssortingng[] args) { //Note, this NotifyDelegate type is defined in the SaveToDatabase project NotifyDelegate nofityDelegate = new NotifyDelegate(NotifyIfComplete); SaveToDatabase sd = new SaveToDatabase(); sd.Start(nofityDelegate); Console.ReadKey(); } //this is the method which will be delegated - the only thing it has in common with the NofityDelegate is that it takes 0 parameters and that it returns void. However, it is these 2 which are essential. It is really important to notice that it writes a variable which, due to no constructor, has not yet been called (so _notice is not initialized yet). private static void NotifyIfComplete() { Console.WriteLine(_notice); } private static ssortingng _notice = "Notified"; } public class SaveToDatabase { public void Start(NotifyDelegate nd) { Console.WriteLine("Yes, I shouldn't write to the console from here, it's just to demonstrate the code executed."); Console.WriteLine("SaveToDatabase Complete"); Console.WriteLine(" "); nd.Invoke(); } } public delegate void NotifyDelegate(); } 

Je vous suggère de parcourir le code et de voir quand _notice est appelé et quand la méthode (delegate) est appelée car cela, je l’espère, rendra les choses très claires.

Cependant, enfin, nous pouvons le rendre plus utile en modifiant le type de délégué pour inclure un paramètre.

 using System.Text; namespace ConsoleApplication1 { //I've made this class private to demonstrate that the SaveToDatabase cannot have any knowledge of this Program class. class Program { static void Main(ssortingng[] args) { SaveToDatabase sd = new SaveToDatabase(); //Please note, that although NotifyIfComplete() takes a ssortingng parameter, we do not declare it - all we want to do is tell C# where the method is so it can be referenced later - we will pass the paramater later. NotifyDelegateWithMessage nofityDelegateWithMessage = new NotifyDelegateWithMessage(NotifyIfComplete); sd.Start(nofityDelegateWithMessage); Console.ReadKey(); } private static void NotifyIfComplete(ssortingng message) { Console.WriteLine(message); } } public class SaveToDatabase { public void Start(NotifyDelegateWithMessage nd) { //To simulate a saving fail or success, I'm just going to check the current time (well, the seconds) and store the value as variable. ssortingng message = ssortingng.Empty; if (DateTime.Now.Second > 30) message = "Saved"; else message = "Failed"; //It is at this point we pass the parameter to our method. nd.Invoke(message); } } public delegate void NotifyDelegateWithMessage(ssortingng message); } 

Je considère les delegates comme des interfaces anonymes . Dans de nombreux cas, vous pouvez les utiliser chaque fois que vous avez besoin d’une interface avec une méthode unique, mais vous ne voulez pas avoir à définir cette interface.

Un délégué est une classe simple utilisée pour pointer vers des méthodes avec une signature spécifique, devenant essentiellement un pointeur de fonction de type sécurisé. Le but d’un délégué est de faciliter le rappel d’une autre méthode (ou de méthodes), une fois celle-ci terminée, de manière structurée.

Bien qu’il soit possible de créer un ensemble complet de codes pour exécuter cette fonctionnalité, vous n’en avez pas besoin non plus. Vous pouvez utiliser un délégué.

Créer un délégué est facile à faire. Identifiez la classe en tant que délégué avec le mot clé “delegate”. Ensuite, spécifiez la signature du type.