Un de mes pépins de mes animaux avec des événements d'élevage en C # est le fait qu'une exception dans un gestionnaire d'événements brisera mon code et empêchera d'être appelé d'autres gestionnaires, si le cassé est arrivé à être appelé d'abord; Dans la plupart des cas, mon code ne pouvait pas se moquer de moins si le code de quelqu'un d'autre qui écoute ses événements est cassé.
J'ai créé une méthode d'extension qui attrape des exceptions: p> bien que Cela signifie que mon code porte sur peu importe, cette méthode n'empêche pas d'un premier gestionnaire d'événements qui jette une exception et empêchant la deuxième et les gestionnaires ultérieurs avertis de l'événement. Je suis à la recherche d'une manière d'itération à travers GetInvococationList pour envelopper chaque manutentionnaire d'événements individuel, mais cela semble inefficace, et je ne suis pas certain de la meilleure façon de le faire, ou même si je devrais l'être. P> En outre, je ne suis vraiment pas à l'aise d'ignorer simplement l'exception ici (et ni FXCop / Resharper pour cette affaire); De manière réaliste, que devraient arriver à l'exception dans ce cas? p> p>
4 Réponses :
Vous ne devez piéger que ces exceptions que vous pouvez gérer. Par exemple, vous pouvez accéder à un fichier et ne pas avoir de privilèges suffisants. Si vous savez que cela peut arriver de temps en temps et que tout ce qui doit se produire est que le cas est enregistré puis piéger cette exception seule em>. P>.
Si votre code ne peut pas gérer l'exception, ne le piègent pas. P>
C'est une circonstance exceptionnelle, il y a donc de bonnes chances que les autres gestionnaires d'événements ne puissent pas "faire leurs affaires" non plus, alors laissez-le se propager à un niveau où il peut être manipulé en toute sécurité. P>
Si le gestionnaire d'événements n'a pas compris l'exception, je ne vois pas de bonne façon que vous puissiez gérer l'exception dans la classe de lancer l'événement. Vous n'avez pas de contexte pour savoir ce que fait le gestionnaire. Il est le plus sûr de laisser tomber l'application difficile dans ce cas, car vous n'avez aucune idée de l'effet secondaire que l'autre gestionnaire d'événements peut avoir causé pour déstabiliser votre application. La leçon ici est que les manutentionnaires d'événements doivent être robustes (gérer leurs propres exceptions), comme les classes que les événements d'incendie ne devraient jamais attraper des exceptions projetées par les gestionnaires. P>
L'échec rapide est génial lorsque vous déboguez, mais pas beaucoup lorsque vous êtes un utilisateur. Pourquoi un plug-in Buggy devrait-il cracer chaque application qu'elle touche?
Si vous avez un plug-in Buggy et que vous souhaitez une véritable architecture robuste, vous devez isoler le plugin afin qu'il ne puisse pas nuire à votre application. Vous pouvez le faire avec une appdomaine ou un processus séparé. Si ce n'est pas isolé, vous ne pouvez pas garantir votre état n'a pas été corrompu et continuer à courir malgré une exception non gérée est une recette pour des défaillances bien pires sur la ligne. Fondamentalement, il est essentiel de fonctionner de manière incorrecte que ne pas fonctionner du tout et si vous ne pouvez pas être sûr que vous pouvez toujours exécuter correctement, il vaut mieux échouer.
@Gabe - Un plug-in buggy devrait planter chaque application qu'il touche, car il s'agit d'un plug-in de buggy. Les gens devraient être informés du fait que c'est buggy, il peut donc être fixé ou évité. Protéger les plug-ins moqueurs à la dépense possible des données de l'utilisateur est une mauvaise politique.
@Dan - Rien n'est défini dans la pierre, il se peut que a b> pour le faire afin que le service ou le travail puisse poursuivre le traitement.
@Chaos, je suis dans ce bateau auparavant, où le coût perçu d'un client de voir un "crash" était telle qu'il y avait une pression pour que l'application fonctionne, à tout prix. Je suis devenu de plus en plus résistant à cette ligne de pensée alors que j'accumule plus d'expérience des types de conséquences laids pouvant résulter de la mise en forme de défaillances inconnues.
@Dan - Ne me trompe pas, je ne mange jamais d'exceptions. C'est juste que les gens jettent ces règles qui disent que, en aucun cas, ils ne devraient être cassés.
@Chaos, je ne pense pas à cela comme une règle tellement qu'un avertissement. Si je trébuche dans un champ de mines (et survivre), alors je pense que cela vaut la peine de mentionner où j'ai trouvé les mines.
Peut-être que je ne suis tout simplement jamais allé dans une situation où l'écrasement d'un utilisateur était en réalité meilleur que de ne pas s'écraser, mais il est difficile pour moi d'imaginer un.
@Gabe - je traite avec cette situation en ce moment. Les mois de données sont manquants car le processus censé stocker les données a échoué silencieusement. Je suis sur le point de libérer une nouvelle version qui n'invalue pas d'erreurs. Maintenant, les problèmes seront abordés lorsqu'ils se produisent plutôt que des mois plus tard, quand il est trop tard.
@Gabe - Cela se résume à cela: s'il est correct pour un morceau de code d'échec en silence, ce morceau de code n'aurait pas dû être mis dans le programme en premier lieu. Si une fonctionnalité est si inutile que personne n'a besoin de savoir quand il se casse, il s'agit d'un gonflement inutile qui devrait être supprimé immédiatement.
Tout d'abord, "ne pas casser" est différent de "échouer silencieusement". Deuxièmement, essayant de créer un répertoire qui existe déjà ou supprimant un fichier qui n'est pas là-bas, il n'y a même pas de bugs, alors pourquoi supposer que toutes les exceptions sont dues à la corruption? Troisièmement, il est connu que le plug-in Flash est la principale cause des accidents du navigateur, mais il n'a pas été corrigé et je ne peux pas l'éviter, donc dans une veine similaire, je ne peux pas demander à mes clients de se fixer ou Évitez les plug-ins buggy des autres fournisseurs.
@Jeffrey L. Whitledge: Je considérerais des choses comme des mises à jour des barres de progression comme quelque chose qui peut légitimement échouer silencieusement, mais ne sont pas inutiles. L'utilisateur i> vous souciez de savoir si les barres de progression sont mises à jour, mais le code i> ne le fera pas. Si un programme permet à l'utilisateur d'ouvrir une fenêtre montrant les états des tâches en cours et que l'utilisateur ferme cette fenêtre de manière asynchrone entraînant l'échec de BeginInvokes, ignorer silencieusement ces erreurs sembleraient tout à fait appropriées.
@supercat - Pourquoi la vitrine de progression lancerait-elle des exceptions simplement parce qu'elle a été fermée? Pourquoi ces exceptions inutiles ne seront-elles pas attrapées et ignorées par le code qui sait qu'ils sont inutiles (c'est-à-dire le code de mise à jour de la barre de progression)? Pourquoi le processus de longue date qui invoque les mises à jour de progrès doit-elle faire la distinction entre des exceptions importantes et des obstacles?
@Jeffery L Whitledge: Mon point principal était qu'il y ait des moments où il est tout à fait approprié que le code utile soit en train d'échouer silencieusement. Un exemple serait un événement de mise à jour de la propriété qui appelle BekinVoke de contrôle avec sémantique ("si vous existez toujours, vous devez afficher X"); Cela peut échouer si le contrôle est éliminé au mauvais moment. De tels auditeurs d'événements qui vont échouer silencieusement devraient saisir et étonner les événements eux-mêmes plutôt que de les laisser échapper; Pour un certain nombre de scénarios de "événement de mise à jour de la propriété", l'échec de l'échec en silence est un comportement raisonnable.
@Jeffery l Whitledge: Dans de nombreux cas, je voudrais que les événements devraient être invoqués de manière à ce qu'une exception projetée par la première n'empêche pas l'exécution du reste, mais une exception lancée devrait se propager une fois que tous les événements ont exécuté (les choses obtiennent gênant si plusieurs événements lancent des exceptions; J'espère qu'il existe une convention standard pour stocker des exceptions excédentaires dans la propriété de données des événements). De nombreuses structures de données nécessitent des événements de mise à jour de propriété pour maintenir la cohérence. Si par ex. Un gestionnaire de mise à jour de la propriété censé mettre à jour un élément d'interface utilisateur envoie une exception ...
@Jeffery l Whitledge: ... et cela empêche l'exécution d'autres gestionnaires de mise à jour de propriété nécessaires pour maintenir la cohérence, cela pourrait entraîner une corruption de données qui aurait été évitée si la remise des événements a été autorisée à conclure avant de se propager l'exception . Bien sûr, si l'on veut invoquer manuellement les gestionnaires d'événements de manière à permettre une transformation complète avant que des exceptions se propagent, il est également possible de conserver également les gestionnaires comme une liste plutôt que comme une multicasttégate.
Et si vous faites quelque chose comme ça?
En fait, j'ai fait foreach (gestionnaire de délégué dans WearNchanged.getinVocationListListListList ())); Code>, mais malheureusement, le gestionnaire
(expéditeur, e) code> donne une erreur dans VS, que "méthode, délégué ou événement est attendue" pour "gestionnaire"; Je me gratte la tête sur celui-ci, c'est clairement un délégué.
@ Flynn1179 - La déléguée par elle-même ne peut être invoquée dans cette affaire. Vous devez soit jeter comme vous le voyez dans mon exemple ou invoquer le délégué comme celui-ci: Handler.dynamicinvoke (expéditeur, E); code>
Aah .. Je suis confus entre "délégué" et "délégué" :)
OK, qui est plus rapide et fait gestionnaire (expéditeur, e) code> ou non, ni ne casse pas
gestionnaire.dynamicinvoke (expéditeur, e) code>? Je sais que je pourrais faire cela avec un test rapide, mais je suis curieux de savoir pourquoi on est plus rapide, ou peut-être mieux
@ Flynn1179 - Comme le nom implique l'appelant dynamicinvoke code> peut prendre des arguments mais au moment de l'exécution, les arguments seront validés. La coulée des délégués à leurs types appropriés supprimera le chèque et augmentera en effet les performances. Les chiffres réels sont difficiles à évaluer car cela dépend du nombre d'abonnés que vous avez et de la fréquence à laquelle cet événement est élevé.
Un petit problème que je viens de découvrir: EventHandler
EventHandler code>; Malheureusement, les classes génériques et non génériques
EventHandler CODE> hériter directement de MulticastDelegate, il semble donc que je n'ai pas d'autre choix que d'aller avec l'option
dynamicinvoke code>.
une règle simple mais importante: p>
Chaque défaut de code devrait être aussi mortel que possible que possible possible. p> blockQuote>
De cette manière, les bugs sont trouvés et fixés, et le logiciel devient plus fort. Ce que les autres disent est correct; Ne jamais attraper une exception que vous n'attendez pas et ne savez pas comment traiter. P>
Tout le monde continue à dire cela, mais ce n'est pas toujours aussi simple. Et si elles n'ont aucun contrôle sur qui souscrit à l'événement? Et s'il est impératif que la transformation continue?
@Chaos, dans ce cas, vous devez structurer vos événements d'une manière qui permet d'isoler. Par exemple, configurez un modèle client / serveur avec des processus isolés. Maintenant, un client peut planter et le serveur peut heureusement à conserver le traitement, même si les clients sont buggy. Si vous avez des événements de rappel simples sans limites d'isolation, il n'ya aucune protection contre un client indiscipliné qui interfère avec votre traitement. Le processus ne peut pas garantir son impératif (continuer à traiter correctement i>) et il est irresponsable pour le processus de prétendre que cela puisse.
Dan: En supposant que c'est tout le code géré, comment vous attendriez-vous à un gestionnaire d'exception Buggy pour interférer avec le processus principal?
@Gabe, je suppose que vous voulez dire un gestionnaire d'événements. Le problème est que cela vit probablement sur une classe qui a accès au contexte partagé. Par exemple, il peut utiliser la base de données ou peut-être envoyer des commandes à un morceau de machinerie. Si une exception survient, il est possible que certains dommages aient déjà été faits, mais si vous ignorez l'exception inconnue, vous autorisez simplement les dommages au composé. Cela dit, vous pouvez minimiser la possibilité de dommages au moyen d'une bonne utilisation des pratiques de codage défensives. Mieux encore, manipulez des exceptions connues sur la couche de service et ils ne se présenteront pas dans le gestionnaire d'événements.
Dan: Oui, je voulais dire un gestionnaire d'événements. Mon point était que l'OP semblait avoir une situation où son code n'était pas couplé au code du gestionnaire d'événements. Je veux dire, pourquoi supposer que les dommages sont limités à l'application et n'écrasent que l'application? Vous ne savez pas quels types de choses horribles que ce gestionnaire d'événements a été liée, alors pourquoi ne pas planter tout le système d'exploitation?
@Gabe, le système d'exploitation crée des limites d'isolation précisément de manière à ce qu'il ne soit pas à graver si un processus échoue. Si quelque chose échoue dans le noyau, toutefois, Windows sera bloqué, Linux Panique du noyau, etc. Il est possible de limiter les causalités, mais nécessite une conception minutieuse et une compréhension de ce qui est contenu. Si l'exception est inconnue, une isolation très significative est requise (comme une appdomaine ou un deuxième processus) pour garantir l'état interne est préservé.
Dan: Il semble y avoir deux raisons principales de crash lorsqu'une exception inconnue frappe: l'une est de vous forcer à le déboguer et l'autre est d'empêcher une corruption supplémentaire. Si vous essayez d'éviter une corruption ultérieure, pourquoi supposer que toute l'application est corrompue? Pourquoi supposer que seule l'application est corrompue? Pourquoi supposer que l'application de l'application ne causera pas de corruption supplémentaire?
@Dan Bryant: J'aurais un moyen que, bien que cela puisse être judicieux pour une exception inattendue qui se produit dans le code qui transforme un objet d'invalidation de l'objet en question (toute autre tentative d'utilisation de l'objet lancera une exception, éventuellement avec une copie enregistrée. de l'exception antérieure) Je ne vois aucune raison d'attendre la corruption dans des objets que le code ne soit pas censé muté. De nombreux événements sont conçus pour ne ménager que des objets à l'extérieur de celui qui les soulevent. Une défaillance de l'un de ce type devait impliquer de l'objet de soulever l'événement, ni des autres gestionnaires.
Je me suis souvent demandé quelle est la meilleure façon de gérer cela est.
Cela soulève réellement une autre question, je vais demander séparément; Dans cette question, je codir du point de vue de la classe élever l'événement, mais je me demande si des gestionnaires d'événements devraient être autorisés à lancer des exceptions en premier lieu. Stackoverflow.com/ Questions / 3114543 / ...