Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
L’article précédent a abordé les modèles d’événements les plus courants. .NET Core a un modèle plus détendu. Dans cette version, la EventHandler<TEventArgs> définition n’a plus la contrainte qui TEventArgs doit être une classe dérivée System.EventArgs.
Cela augmente la flexibilité pour vous et est rétrocompatible. Commençons par la flexibilité. L'implémentation de System.EventArgs utilise une méthode définie dans System.Object, qui est la méthode : MemberwiseClone(), et qui crée une copie superficielle de l'objet. Cette méthode doit utiliser la réflexion pour implémenter ses fonctionnalités pour n’importe quelle classe dérivée de EventArgs. Cette fonctionnalité est plus facile à créer dans une classe dérivée spécifique. Cela signifie efficacement que la dérivation de System.EventArgs est une contrainte qui limite vos conceptions, mais ne fournit aucun avantage supplémentaire. En fait, vous pouvez modifier les définitions de FileFoundArgs et SearchDirectoryArgs de sorte qu’elles ne dérivent pas de EventArgs. Le programme fonctionne exactement de la même façon.
Vous pourriez également changer le SearchDirectoryArgs en un struct, si vous faites une autre modification :
internal struct SearchDirectoryArgs
{
internal string CurrentSearchDirectory { get; }
internal int TotalDirs { get; }
internal int CompletedDirs { get; }
internal SearchDirectoryArgs(string dir, int totalDirs, int completedDirs) : this()
{
CurrentSearchDirectory = dir;
TotalDirs = totalDirs;
CompletedDirs = completedDirs;
}
}
La modification supplémentaire consiste à appeler d'abord le constructeur sans paramètre avant d'accéder à celui qui initialise tous les champs. Sans cet ajout, les règles de C# signalent que les propriétés sont accessibles avant d’être affectées.
Vous ne devez pas remplacer une FileFoundArgs classe (type référence) par un struct (type valeur). Le protocole de gestion de l’annulation nécessite que vous transmettiez par référence des arguments d’événement. Si vous avez apporté la même modification, la classe de recherche de fichiers n’a jamais pu observer les modifications apportées par l’un des abonnés aux événements. Une nouvelle copie de la structure serait utilisée pour chaque abonné, et cette copie serait une copie différente de celle affichée par l’objet de recherche de fichiers.
Ensuite, examinons la façon dont cette modification peut être rétrocompatible. La suppression de la contrainte n’affecte aucun code existant. Tous les types d’arguments d’événement existants dérivent toujours de System.EventArgs. La compatibilité rétroactive est une raison majeure pour laquelle ils continuent de dériver de System.EventArgs. Tous les abonnés aux événements existants sont des abonnés à un événement qui a suivi le modèle classique.
À la suite d’une logique similaire, tout type d’argument d’événement créé n’aurait plus d’abonnés dans des bases de code existantes. Les nouveaux types d'événements qui ne dérivent pas de System.EventArgs ne brisent pas ces bases de code.
Événements avec les abonnés Async
Vous avez un modèle final à découvrir : comment écrire correctement des abonnés à l’événement qui appellent du code asynchrone. Le défi est décrit dans l’article sur async et await. Les méthodes asynchrones peuvent avoir un type de retour void, mais cela est déconseillé. Quand votre code pour l’abonné à l’événement appelle une méthode asynchrone, vous n’avez d’autre choix que de créer une méthode async void. La signature du gestionnaire d’événements l’exige.
Vous devez réconcilier ces conseils opposés. En quelque sorte, vous devez créer une méthode sécurisée async void . Les principes de base du modèle que vous devez implémenter sont indiqués dans le code suivant :
worker.StartWorking += async (sender, eventArgs) =>
{
try
{
await DoWorkAsync();
}
catch (Exception e)
{
//Some form of logging.
Console.WriteLine($"Async task failure: {e.ToString()}");
// Consider gracefully, and quickly exiting.
}
};
Tout d’abord, notez que le gestionnaire est marqué comme gestionnaire asynchrone. Étant donné qu’il est affecté à un type délégué de gestionnaire d’événements, il a un type de retour void. Cela signifie que vous devez suivre le modèle indiqué dans le gestionnaire et ne pas permettre aux exceptions d'être lancées hors du contexte du gestionnaire asynchrone. Comme il ne retourne pas une tâche, aucune tâche ne peut signaler l’erreur en passant à l’état d’erreur. Étant donné que la méthode est asynchrone, la méthode ne peut pas lever l’exception. (La méthode appelante continue l’exécution, car elle est async.) Le comportement réel du runtime est défini différemment pour différents environnements. Il peut mettre fin au thread ou au processus propriétaire du thread, ou laisser le processus dans un état indéterminé. Tous ces résultats potentiels sont très indésirables.
Vous devez encapsuler l’expression await pour la tâche asynchrone dans votre propre bloc try. S’il provoque une tâche défectueuse, vous pouvez consigner l’erreur. S’il s’agit d’une erreur à partir de laquelle votre application ne peut pas récupérer, vous pouvez quitter le programme rapidement et correctement
Cet article a expliqué les principales mises à jour du modèle d’événement .NET. Vous pouvez voir de nombreux exemples des versions antérieures dans les bibliothèques avec lesquelles vous travaillez. Toutefois, vous devez également comprendre quelles sont les dernières tendances. Vous pouvez voir le code terminé de l’exemple à Program.cs.
L’article suivant de cette série vous aide à faire la distinction entre l’utilisation de delegates et de events dans vos conceptions. Ils sont des concepts similaires et cet article vous aide à prendre la meilleure décision pour vos programmes.