Délégués et événements en C #

La traduction de l'article a été préparée spécialement pour les étudiants du cours "Développeur C #" .




Quels sont les événements en C #?


Un Ă©vĂ©nement peut ĂȘtre utilisĂ© pour fournir des notifications. Vous pouvez vous abonner Ă  l'Ă©vĂ©nement si vous avez besoin de ces notifications. Vous pouvez Ă©galement crĂ©er vos propres Ă©vĂ©nements qui vous informeront que quelque chose qui vous intĂ©resse s'est produit. Le .NET Framework propose des types intĂ©grĂ©s que vous pouvez utiliser pour crĂ©er des Ă©vĂ©nements. À l'aide de dĂ©lĂ©guĂ©s, d'expressions lambda et de mĂ©thodes anonymes, vous pouvez crĂ©er et utiliser des Ă©vĂ©nements de maniĂšre pratique.

Comprendre les délégués en C #


En C #, les dĂ©lĂ©guĂ©s forment les Ă©lĂ©ments de base des Ă©vĂ©nements. Un dĂ©lĂ©guĂ© est un type qui dĂ©finit la signature d'une mĂ©thode. Par exemple, en C ++, cela peut ĂȘtre fait Ă  l'aide d'un pointeur de fonction. En C #, vous pouvez crĂ©er une instance d'un dĂ©lĂ©guĂ© qui pointe vers une autre mĂ©thode. Vous pouvez appeler cette mĂ©thode via l'instance dĂ©lĂ©guĂ©e.

Voici un exemple de déclaration d'un délégué et d'appel d'une méthode par son intermédiaire.

Utilisation d'un délégué en C #


}class Program { public delegate double MathDelegate(double value1, double value2); public static double Add(double value1, double value2) { return value1 + value2; } public static double Subtract(double value1, double value2) { return value1 - value2; } Console.ReadLine(); public static void Main() { MathDelegate mathDelegate = Add; var result = mathDelegate(5, 2); Console.WriteLine(result); // : 7 mathDelegate = Subtract; result = mathDelegate(5, 2); Console.WriteLine(result); // : 3 } 

Comme vous pouvez le voir, nous utilisons le mot clé delegate pour indiquer au compilateur que nous créons un type délégué.

Il est facile d'instancier des délégués avec la création automatique d'un nouveau type de délégué.

Vous pouvez également utiliser le nouveau mot clé pour créer un délégué.

MathDelegate mathDelegate = new MathDelegate(Add) ;

Un délégué instancié est un objet; Vous pouvez également l'utiliser et passer comme argument à d'autres méthodes.

Délégués de multidiffusion en C #


Une autre grande caractĂ©ristique des dĂ©lĂ©guĂ©s est que vous pouvez les combiner ensemble. C'est ce qu'on appelle la multidiffusion. Vous pouvez utiliser l'opĂ©rateur + ou + = pour ajouter une autre mĂ©thode Ă  la liste d'appels d'une instance de dĂ©lĂ©guĂ© existante. De mĂȘme, vous pouvez Ă©galement supprimer une mĂ©thode de la liste d'appels Ă  l'aide de l'opĂ©rateur d'affectation de dĂ©crĂ©mentation (- ou - =). Cette fonctionnalitĂ© sert de base aux Ă©vĂ©nements en C #. Voici un exemple de dĂ©lĂ©guĂ© de multidiffusion.

 class Program { static void Hello(string s) { Console.WriteLine(" Hello, {0}!", s); } static void Goodbye(string s) { Console.WriteLine(" Goodbye, {0}!", s); } delegate void Del(string s); static void Main() { Del a, b, c, d; //   a    Hello: a = Hello; //   b    Goodbye: b = Goodbye; //    a  b - c: c = a + b; //  a    c,   d,       Goodbye: d = c - a; Console.WriteLine("Invoking delegate a:"); a("A"); Console.WriteLine("Invoking delegate b:"); b("B"); Console.WriteLine("Invoking delegate c:"); c("C"); Console.WriteLine("Invoking delegate d:"); d("D"); /* : Invoking delegate a: Hello, A! Invoking delegate b: Goodbye, B! Invoking delegate c: Hello, C! Goodbye, C! Invoking delegate d: Goodbye, D! */ Console.ReadLine(); } } 

Cela est possible car les délégués héritent de la classe System.MulticastDelegate , qui à son tour hérite de System.Delegate . Vous pouvez utiliser les membres définis dans ces classes de base pour vos délégués.

Par exemple, pour savoir combien de méthodes un délégué de multidiffusion appellera, vous pouvez utiliser le code suivant:

 int invocationCount = d.GetInvocationList().GetLength(0); 

Covariance et contravariance en C #


Lorsque vous affectez une méthode à un délégué, la signature de la méthode ne doit pas nécessairement correspondre exactement au délégué. C'est ce qu'on appelle la covariance et la contravariance. La covariance permet à une méthode d'avoir un type de retour plus dérivé que celui défini dans le délégué. La contravariance permet une méthode avec des types de paramÚtres moins dérivés que les types du délégué.

Covariance des délégués


Voici un exemple de covariance,

 class Program { public delegate TextWriter CovarianceDel(); public static StreamWriter MethodStream() { return null; } public static StringWriter MethodString() { return null; } static void Main() { CovarianceDel del; del = MethodStream; del = MethodString; Console.ReadLine(); } } 

Étant donnĂ© que StreamWriter et StringWriter hĂ©ritent de TextWriter , vous pouvez utiliser CovarianceDel avec les deux mĂ©thodes.

Contravariance chez les délégués


Ce qui suit est un exemple de contravariance.

 class Program { public static void DoSomething(TextWriter textWriter) { } public delegate void ContravarianceDel(StreamWriter streamWriter); static void Main() { ContravarianceDel del = DoSomething; Console.ReadLine(); } } 

Étant donnĂ© que la mĂ©thode DoSomething peut fonctionner avec TextWriter , elle peut certainement fonctionner avec StreamWriter . En raison de la contravariance, vous pouvez appeler un dĂ©lĂ©guĂ© et transmettre une instance de StreamWriter Ă  la mĂ©thode DoSomething .

Vous pouvez en savoir plus sur ce concept ici .

Expressions lambda en C #


Parfois, une signature de mĂ©thode entiĂšre peut nĂ©cessiter plus de code que le corps de la mĂ©thode elle-mĂȘme. Il existe Ă©galement des situations dans lesquelles vous devez crĂ©er une mĂ©thode entiĂšre juste pour l'utiliser en tant que dĂ©lĂ©guĂ©.

Pour ces cas, Microsoft a ajouté de nouvelles fonctionnalités en C #, telles que des méthodes anonymes dans 2.0. En C # 3.0, les choses se sont encore améliorées lorsque des expressions lambda ont été ajoutées. L'expression lambda est la méthode préférée lors de l'écriture de nouveau code.

Voici un exemple de la derniĂšre syntaxe lambda.

 class Program { public delegate double MathDelegate(double value1, double value2); public static void Main() { MathDelegate mathDelegate = (x,y) => x + y; var result = mathDelegate(5, 2); Console.WriteLine(result); // : 7 mathDelegate = (x, y) => x - y; ; result = mathDelegate(5, 2); Console.WriteLine(result); // : 3 Console.ReadLine(); } } 

Pour lire ce code, vous devez utiliser le mot «suit» dans le contexte de la syntaxe lambda spéciale. Par exemple, la premiÚre expression lambda dans l'exemple ci-dessus se lit «x et y suivent l'addition de x et y».

Une fonction lambda n'a pas de nom spécifique, contrairement à une méthode. Pour cette raison, les lambdas sont appelées fonctions anonymes. Vous n'avez également pas besoin de spécifier explicitement le type de la valeur de retour. Le compilateur l'assume automatiquement à partir de votre lambda. Et dans le cas de l'exemple ci-dessus, les types de paramÚtres x et y ne sont pas non plus explicitement spécifiés.

Vous pouvez créer des lambdas qui s'étendent sur plusieurs opérateurs. Vous pouvez le faire en ajoutant des accolades autour des instructions qui composent le lambda, comme indiqué dans l'exemple ci-dessous.

 MathDelegate mathDelegate = (x,y) => { Console.WriteLine("Add"); return x + y; }; 

Parfois, l'annonce d'un délégué pour un événement semble un peu lourde. Pour cette raison, le .NET Framework a plusieurs types de délégués intégrés que vous pouvez utiliser lors de la déclaration de délégués. Dans l'exemple MathDelegate, vous avez utilisé le délégué suivant:

 public delegate double MathDelegate(double value1, double value2); 

Vous pouvez remplacer ce délégué par l'un des types intégrés, à savoir Func <int, int, int> .

comme ça

 class Program { public static void Main() { Func<int, int, int> mathDelegate = (x,y) => { Console.WriteLine("Add"); return x + y; }; var result = mathDelegate(5, 2); Console.WriteLine(result); // : 7 mathDelegate = (x, y) => x - y; ; result = mathDelegate(5, 2); Console.WriteLine(result); // : 3 Console.ReadLine(); } } 

Les types Func <...> peuvent ĂȘtre trouvĂ©s dans l'espace de noms System. Ils reprĂ©sentent les dĂ©lĂ©guĂ©s qui retournent un type et prennent de 0 Ă  16 paramĂštres. Tous ces types sont hĂ©ritĂ©s de System.MulticaseDelegate afin que vous puissiez ajouter plusieurs mĂ©thodes Ă  la liste d'appels.

Si vous avez besoin d'un type délégué qui ne renvoie pas de valeur, vous pouvez utiliser les types System.Action. Ils peuvent également prendre de 0 à 16 paramÚtres, mais ne renvoient pas de valeur.

Voici un exemple d'utilisation du type Action,

 class Program { public static void Main() { Action<int, int> mathDelegate = (x,y) => { Console.WriteLine(x + y); }; mathDelegate(5, 2); // : 7 mathDelegate = (x, y) => Console.WriteLine(x - y) ; mathDelegate(5, 2); // : 3 Console.ReadLine(); } } 

Vous pouvez en savoir plus sur les délégués intégrés .NET ici .

Les choses se compliquent lorsque votre fonction lambda commence à se référer à des variables déclarées en dehors de l'expression lambda ou à ceci. En rÚgle générale, lorsqu'un contrÎle quitte la portée d'une variable, la variable devient non valide. Mais que faire si le délégué fait référence à une variable locale. Pour résoudre ce problÚme, le compilateur génÚre du code qui prolonge la durée de vie de la variable capturée, au moins aussi longtemps que le délégué a la durée de vie la plus longue. C'est ce qu'on appelle une fermeture.

Vous pouvez en savoir plus sur les fermetures ici .

ÉvĂ©nements en C #


Considérez un modÚle de développement populaire - éditeur-abonné (pub / sub). Vous pouvez vous abonner à un événement, puis vous serez averti lorsque l'éditeur de l'événement lancera un nouvel événement. Ce systÚme est utilisé pour établir une communication faible entre les composants d'une application.

Le délégué forme la base du systÚme d'événements en C #.

Un Ă©vĂ©nement est un type spĂ©cial de dĂ©lĂ©guĂ© qui facilite la programmation orientĂ©e Ă©vĂ©nement. Les Ă©vĂ©nements sont membres d'une classe qui ne peut pas ĂȘtre appelĂ©e en dehors de la classe, quel que soit le spĂ©cificateur d'accĂšs. Ainsi, par exemple, un Ă©vĂ©nement dĂ©clarĂ© public permettrait aux autres classes d'utiliser + = et - = pour cet Ă©vĂ©nement, mais le dĂ©clenchement d'un Ă©vĂ©nement (c'est-Ă -dire l'appel d'un dĂ©lĂ©guĂ©) n'est autorisĂ© que dans la classe contenant l'Ă©vĂ©nement. Regardons un exemple,

 // -  Pub public class Pub { // OnChange    callback-  public event Action OnChange = delegate { }; public void Raise() { // OnChange OnChange(); } } 

Ensuite, une méthode d'une autre classe peut s'abonner à l'événement en ajoutant l'une de ses méthodes au délégué d'événement:

Voici un exemple montrant comment une classe peut fournir un délégué ouvert et générer un événement.

 class Program { static void Main(string[] args) { //   pub Pub p = new Pub(); //  Subscriber 1   OnChange p.OnChange += () => Console.WriteLine("Subscriber 1!"); //  Subscriber 2   OnChange p.OnChange += () => Console.WriteLine("Subscriber 2!"); //  p.Raise(); //   Raise()   callback-     Console.WriteLine("Press enter to terminate!"); Console.ReadLine(); } } 

MĂȘme si l'Ă©vĂ©nement est dĂ©clarĂ© public , il ne peut ĂȘtre dĂ©clenchĂ© directement ailleurs que dans la classe dans laquelle il se trouve.

En utilisant le mot-clé d' event , le compilateur protÚge notre champ contre les accÚs indésirables.

Et aussi

Il ne permet pas l'utilisation de = (affectation directe de délégué). Par conséquent, votre code est désormais protégé contre le risque de supprimer les abonnés précédents en utilisant = au lieu de + =.

En outre, vous remarquerez peut-ĂȘtre la syntaxe spĂ©ciale d'initialisation du champ OnChange pour un dĂ©lĂ©guĂ© vide, tel que delegate { } . Cela garantit que notre champ OnChange n'est jamais nul. Par consĂ©quent, nous pouvons supprimer la vĂ©rification null avant d'appeler l'Ă©vĂ©nement s'il n'y a aucun autre membre de classe qui le rend nul.

Lorsque vous exécutez le programme ci-dessus, votre code crée une nouvelle instance de Pub, s'abonne à l'événement de deux maniÚres différentes et génÚre un événement en appelant p.Raise. La classe Pub n'est complÚtement au courant d'aucun des abonnés. Il génÚre juste un événement.

Vous pouvez également lire mon article, C # Publisher-Subscriber Design Pattern, pour une compréhension plus approfondie de ce concept.

Eh bien, c'est tout pour l'instant. J'espÚre que vous comprenez l'idée. Merci d'avoir lu le post. Veuillez me faire savoir s'il y a des erreurs ou des changements sont nécessaires dans les commentaires ci-dessous. Merci d'avance!

Liens utiles


www.c-sharpcorner.com/blogs/c-sharp-generic-delegates-func-action-and-predicate
docs.microsoft.com/en-us/dotnet/csharp/programming-guide/concepts/covariance-contravariance
https://web.archive.org/web/20150707082707/http://diditwith.net/PermaLink,guid,235646ae-3476-4893-899d-105e4d48c25b.aspx

Source: https://habr.com/ru/post/fr472094/


All Articles