Un modèle commun dans la framework .NET est le motif Tryxxx (je ne sais pas si c'est ce qu'ils l'appellent vraiment), dans lequel la méthode appelée tente de faire quelque chose, retourner de la documentation de ces méthodes disent qu'ils ne lanceront pas d'exceptions: cette défaillance sera signalée dans la valeur de retour de la méthode. Tout va bien jusqu'à présent. P> J'ai récemment rencontré deux circonstances différentes dans lesquelles .NET Framework Methodes implémente le modèle TryXXX a lancé des exceptions. Voir Constructeur System.Random? et uri.trycreate jette uformatexception? pour plus de détails. p> dans les deux cas, le Mettez un autre moyen, si vous écrivez vrai code> si C'était un succès ou
faux code> si l'opération a échoué. Un bon exemple est le "code> dictionary.trygevalue code> méthode"
tryxxx code> Méthode appelée d'autres méthodes qui ont lancé des exceptions inattendues et ces exceptions échappées. Ma question: Est-ce que cela enfreint le contrat implicite de ne pas jeter d'exceptions? P>
tryfoo code>, garantit-ils que les exceptions ne peuvent pas échapper, en l'écrivant comme Ceci? P>
public bool TryFoo(string param, out FooThing foo)
{
try
{
// do whatever
return true;
}
catch
{
foo = null;
return false;
}
}
12 Réponses :
L'idée derrière Il est tentant, car cette garantie aucune exception ne s'échappera, honorant ainsi le contrat implicite. P>
blockQuote>
Ce n'est pas complètement vrai - il y a quelques exceptions près que peut em> échapper à un tryxxx code> est qu'il ne jettera pas un type d'exception spécifique qui se produira normalement si vous appelez simplement l'appel correspondant
xxx code> ". sait qu'ils ignorent toute exception qui se produirait normalement. p>
try / attrape code> (par exemple,
OutofMemoryException code>,
StackoverflowException code>, etc.). p>
n'importe quelle exception i>? Et si "trygetvalue" n'a pas lancé lorsque l'appel GetHashCode sur la clé transbordée a jeté une exception?
Eh bien non, pas aucune exception i>. Je vais clarifier.
Vous ne devriez presque jamais attraper toutes les exceptions. Si vous C'est ok pour ESSYXXX CODE> À l'aide d'un
ESSAYER CODE> Paire autour de XXX (plutôt que le préféré, XXX implémenté en termes de tryxxx et de lancer sur false Résultat), attrapez la ou les exceptions prévues spécifiques. P>
tryxxx code> pour lancer s'il existe un problème sémantiquement différent, tel qu'une erreur de programmeur (peut-être passer NULL où il n'est pas prévu). P>
Cela dépend. en python (ours avec moi), vous pouvez causer beaucoup de problèmes en attrapant Ma règle ecoutetique est que vous devez utiliser Tryparsefoo Code> devrait attraper toutes les exceptions à analyser les trucs comme des chaînes nulles ou malformatées. Mais il existe également des exceptions non liées (
threadabortexception code>, disons) qui ne doivent pas être automatiquement calmes. P>
KeyboardInterrupter CODE> Exceptions, ce qui signifie essentiellement que votre programme continue de fonctionner même si vous êtes hors de celui-ci. P >
tryfoo code> lorsque vous souhaitez
foo code> sans nettoyer vos entrées. Si
tryfoo code> attrape des erreurs non liées à
foo code> à votre entrée, il est trop agressif. P>
+1: Attraper toutes les exceptions juste parce que c'est un essai ... La méthode n'a pas de sens. Il devrait gérer toutes les exceptions liées au mécanisme d'analyse. Beaucoup considèrent une déclaration de capture vide ou une exception de capture (la classe de base) une mauvaise pratique. Je suis d'accord, c'est pourquoi ils s'appellent des exceptions (pas parce que vous les attendez déjà).
Cela dépend de ce que la méthode tente de faire. P>
Si la méthode par exemple lit une valeur de chaîne à partir d'un lecteur de données et tente de l'analyser dans un entier, il ne doit pas lancer une exception si l'analyse échoue, mais elle devrait lancer une exception si le lecteur de données ne peut pas lire à partir de la base de données ou si la valeur que vous lisez n'est pas une chaîne du tout. P>
La méthode devrait suivre le principe général pour accroître les exceptions que vous ne devez attraper que ce que vous savez comment gérer. Si une exception complètement différente se produit, vous devez laisser cette bulle jusqu'à un autre code qui sait comment le gérer. P>
Mettez un autre moyen, si vous écrivez tryfoo, garantiriez-vous que des exceptions ne peuvent pas échapper, par l'écrire comme ça? P>
Non, je n'invaluerais pas d'exceptions dans ma méthode tryfoo. Foo dépend de Tryfoo, pas l'inverse. Puisque vous avez mentionné le dictionnaire générique, qui a une méthode getvalue et une méthode trygetvalue, j'écrirais mes méthodes de dictionnaire comme suit: p>
xxx pré> donc, getvalue dépend de la trygetvalue et jette un Exception si trygetvalue retourne faux. C'est bien mieux que d'appeler GetValue à partir de trygetvalue et d'avaler l'exception résultante. P> blockQuote>
Oui, je comprends ce modèle. Mais que se passe-t-il si clé.gethashcode () jette une exception pour une raison quelconque? Votre affirmation est que TRYGETVALUE devrait laisser cette exception s'échapper? Au fait, votre méthode GetValue devrait vous renvoyer plutôt que Bool.
Oui, si GetHashCode jette une exception, l'exception devrait s'échapper. La clé ici est la portée et la séparation des préoccupations: trygetvalue teste en toute sécurité si une clé existe dans un dictionnaire, il ne teste pas que la méthode GetHashCode est correctement mise en œuvre.
Mais si tryfoo permet de s'échapper à quelques exceptions, alors ce qu'elle dit vraiment est: "Je ne jetterai aucune exception que je sais que je sais comment gérer", puis toute l'idée du contrat "Je ne vais pas lancer d'exceptions" est jeté de la fenêtre. P> blockQuote>
Je pense que vous l'avez dit ici ici :) p>
tryxxx ne devrait garantir que s'il ne lancera pas d'exceptions, il sait manipuler. Si cela ne sait pas comment gérer une exception donnée, pourquoi devrait-il l'attraper? La même chose vaut pour vous - si vous ne savez pas quoi faire, pourquoi l'attraperiez-vous? BadallOcExceptions par exemple, bien, (USsudialement) pas beaucoup à faire à propos de ceux-ci, sauf pour les attraper dans votre mode principal Essayer / Catch, montrez un message d'erreur et essayez de fermer l'application gracieusement .. p>
Les deux autres questions de StackoverFlow que vous avez liées pour sembler être des bugs authentiques. Celui où uri.trycreate jette une exception uformatexception pour L'exemple est définitivement une pause dans le contrat, car la documentation MSDN pour cette méthode indique explicitement: p>
ne jette pas une exception si l'URI ne peut pas être créée. P> blockQuote>
L'autre, TryDequeue, je ne trouve aucune documentation pour, mais cela semble aussi être un bug. Si les méthodes étaient appelées tryxxx em> mais la documentation indiquait explicitement qu'ils pouvaient lancer des exceptions, vous pourriez avoir un point. P>
généralement, il y aura deux versions de ce type de méthode, qui jette des exceptions et une seule. Par exemple, int32.parse et int32.trypsarse . Le point de la méthode tryxxx em> n'est pas de masquer em> exceptions. C'est pour des situations où l'échec n'est pas en réalité une condition exceptionnelle. Par exemple, si vous lisez dans un numéro de la console et que vous souhaitez continuer à essayer jusqu'à ce qu'un nombre correct soit en entrée, vous ne voulez pas continuer à attraper des exceptions. De même, si vous souhaitez obtenir une valeur d'un dictionnaire, mais c'est un cas parfaitement normal qu'il pourrait n'existerait pas, vous utiliseriez trygetvalue . p>
résumé strong>: tryxxx em> ne doit pas lancer d'exceptions et il n'est pas conçu pour cacher des exceptions non plus. C'est pour les cas où il est normal et non exceptionnel d'échouer, et vous souhaitez détecter ce cas facilement sans les frais généraux et l'effort d'attraper une exception. Habituellement, une méthode non tryxxx, qui fournit des exceptions, est également fournie, car les cas où il est exceptionnel. P>
J'ai besoin de faire un suivi sur l'exception Uri.trycreate. C'est un cas très intéressant.
attraper et avaler toutes les exceptions comme c'est réalisé dans votre exemple est généralement une mauvaise idée. Quelques exceptions, notamment Phil Haack a un Entrée de blog à propos de ce sujet exact. p> threadabortexception code> et
OutofMemoryException code> ne doit pas être avalé. En fait, le premier ne peut pas être avalé et sera automatiquement retiré à la fin de votre bloc de capture, mais toujours. P>
Oh, je n'écris pas de code comme ça. Je comprends parfaitement que c'est une mauvaise idée. C'était juste un exemple.
Je ne pense pas que tryfoo code> ne doit pas forte> lancer des exceptions. Je pense que le contrat de
tryfoo code> est qu'il gérera une entrée non valide comme un cas normal, non exceptionnelle. C'est-à-dire que cela ne jettera pas une exception si elle échoue à sa tâche en raison d'une entrée non valide; S'il échoue pour toute autre raison, il devrait lancer une exception. Mon attente après
tryfoo code> Les retours sont que c'est-à-dire manipulé mon entrée ou mon entrée était invalide em>. Si aucune de ces choses n'est vraie, je veux une exception, alors je sais à ce sujet. P>
Ce n'est pas une garantie de ne pas lancer d'exceptions. Considérez cet exemple trivial sur le dictionnaire ... J'ai envoyé une clé null, je reçois une nullargumentexception. Le réflecteur montre le code davantage ... p> dans mon esprit la signification du contrat est "Si ce que vous avez essayé de faire échoué, je ne vais pas jeter une exception", Mais c'est loin de "je ne lancerai aucune exception." Puisque vous n'écrivez probablement pas un cadre, vous pourriez faire des compromis avec quelque chose comme ça ... p> ... et ne gère pas le cas de panne de tryxxx avec cette prise Bloc (de sorte que vous n'avez pas de tas de journaux triviaux; Dans ce cas, vous aurez au moins révéler la nature du bogue et l'identifiera pendant le développement. temps ainsi que la note à l'exécution. p> p>
J'ai déjà assisté à une grande conversation par une personne qui a dit qu'il avait participé à la stratégie de traitement des exceptions pour la BCL. Malheureusement, j'ai oublié son nom et je ne trouve pas mes notes. P>
Il a décrit la stratégie ainsi: p>
Le point intéressant ici était # 4. Je me souviens clairement de lui indiquant le mode de défaillance unique fort> partie des directives. D'autres échecs doivent toujours lancer des exceptions. P>
Incidemment, il a également déclaré que l'équipe CLR lui a dit que la raison pour laquelle les exceptions .NET sont lentes, c'est parce qu'elles sont implémentées au-dessus de Seh. Ils ont également déclaré qu'il n'était pas nécessaire de la mettre en œuvre de cette façon (en dehors de l'opportunité), et s'ils se sont rendus dans le top 10 des problèmes de performance réels pour les clients réels, ils penseraient à la ré-mise en œuvre à être plus rapide! p>
Premier gardien à l'esprit Quelle exception signifie en premier lieu:
Une exception signifie que votre méthode ne peut pas faire ce que son nom dit qu'il le fait. strong> P> La raison pour laquelle nous implémentons le modèle L'idée derrière le modèle code> tryfoo code> est que votre méthode doit pouvoir indiquer le mode de défaillance EM> particulièrement fréquent sans lancer une exception. Par conséquent, vous devriez tryfoo code> en premier lieu est que jeter des exceptions est coûteux et si vous demandez au débogueur de se casser lorsqu'une exception est lancée, elle peut être très ennuyeux em>. Par conséquent, si vous avez une méthode avec un mode de défaillance particulièrement courant, tel que
int32.parse code>, qui jette un
argumentException code> lorsqu'il a été donné une entrée non valide, ce sera problématique. . p>
bool TryFoo() {
try {
Foo();
return true;
}
catch (SomeKindOfException) {
return false;
}
}