Y at-il une raison du monde réel à utiliser jeter ex?

En C #, throw ex est presque toujours erroné, car il réinitialise la trace de la stack.

Je me demande juste, y a-t-il une utilisation du monde réel pour cela? La seule raison pour laquelle je peux penser est de cacher les éléments internes de votre bibliothèque fermée, mais c’est une raison très faible. En dehors de cela, je n’ai jamais rencontré dans le monde réel.

Edit: Je veux dire throw ex , comme en lançant exactement la même exception qui a été capturée, mais avec un stacktrace vide, comme en le faisant tout à fait faux. Je sais que le jet ex doit exister en tant que construction de langage pour permettre de lancer une exception différente ( throw new DifferentException("ex as innerException", ex) ) et je me demandais s’il existait une situration où un throw ex n’était pas mauvais.

Il n’y a aucune raison valable de relancer les objects d’exception, à savoir ‘throw ex’.

C’est une fonctionnalité language \ comstackr qui est possible mais qui n’ajoute aucune valeur pratique à l’effet négatif qu’elle a. C’est une situation similaire à celle de pouvoir écrire du code pour accéder aux membres sur des références nulles – c’est bien sûr possible mais sans valeur, par exemple:

  //Possible but not useful. object ref1 = null; ref1.ToSsortingng(); 

Pouvoir relancer des objects d’exception est malheureux et très souvent mal compris par les débutants et les programmeurs expérimentés, c’est-à-dire jusqu’à l’obtention d’une exception non gérée en production avec une trace de stack tronquée enregistrée.

Il existe un sentiment ténu selon lequel il pourrait être utilisé pour masquer intentionnellement des informations de trace de stack, mais le lancement d’une nouvelle exception constituerait le moyen approprié de procéder.

J’irais même jusqu’à dire que pouvoir relancer des objects d’exception (c’est-à-dire ‘throw ex’) est un défaut de conception – il est trop facile de faire la mauvaise chose. Cependant, je soupçonne qu’il s’agit là d’un compromis de conception dans le compilateur pour des raisons de performances, car cela impliquerait un temps système supplémentaire pour valider que «throw ex» n’est pas un «re-lancer». Je suis sûr qu’il y a beaucoup de compromis de ce genre.

Je ne l’ai jamais vu utilisé à dessein, mais cela ne veut évidemment rien dire.

Regardons les alternatives:

 catch(Exception ex) { // do stuff (logging) throw; // a) continue ex throw new SomeException("blah", ex); // b) wrap throw new SomeException("blah"); // c) replace throw ex; // d) reset stack-trace } 

C) et d) abandonnent tous deux la trace de stack, le seul ‘avantage’ de d) que je peux voir est que le throw ex; conserve le type exact et les éventuelles propriétés supplémentaires (erreur SQL) de ex .

Ainsi, y a-t-il un cas où vous souhaitez conserver toutes les informations d’une exception à l’exception de la stack-trace? Pas normalement, mais quelques spéculations:

  • à la limite d’une bibliothèque obfusquée. Mais c) semblerait être une meilleure option ici.
  • sur le site d’appel à du code généré, comme dans la classe RegEx.

Si vous vous arrêtez pour y penser, throw ex est en fait utilisé assez fréquemment – sauf avec une variable locale non nommée. Il serait assez difficile de (re) concevoir le langage de sorte que:

 throw new ArgumentException(); 

était valide, mais

 var ex = new ArgumentException(); throw ex; 

était invalide (et ensuite cela pour tracer des affectations de variables plus complexes). Ie throw ex est la forme “normale” de la déclaration de projection, mais elle est généralement erronée lors de la relance d’une exception déjà interceptée.

throw ex est juste une erreur ou une faute de frappe faite par des personnes qui ne connaissent pas ou oublient le throw; .

Il ne peut être prévu que dans un cas, signalé par d’autres personnes dans leurs réponses: la situation dans laquelle vous ne souhaitez pas que la trace de stack appropriée soit envoyée à l’appelant, tout en conservant le type de l’exception précédemment levée. . Dans la pratique, je peux difficilement imaginer le cas où quelqu’un en aura besoin et n’utilisera pas la throw new SomeCustomException(...) place.

La règle FxCop CA2200 RethrowToPreserveStackDetails va dans le même sens, vous invitant à retransmettre une exception correctement à l’aide de throw; :

 try { // ... } catch (SomeException ex) { Logger.AddException(ex); throw; } 

Voici deux applications du monde réel pour renvoyer des objects d’exception existants:

Réflexion

Lorsque vous appelez du code par reflection, toutes les exceptions générées sont encapsulées dans TargetInvocationException . Mais vous voudrez peut-être / aurez besoin de gérer les exceptions au-delà de la “barrière” de reflection. Dans ce cas, le wrapping compliquera le code de traitement des exceptions. Au lieu de cela, vous pouvez décompresser l’exception d’origine et la renvoyer.

Si vous êtes sur .NET 4.5+, vous pouvez utiliser ExceptionDispatchInfo pour conserver la trace de la stack d’origine:

 MethodInfo someMethod = ... try { someMethod.Invoke(); } catch (TargetInvocationException e) { ExceptionDispatchInfo.Capture(e.InnerException).Throw(); } 

API de tâche

Dans l’API de tâche .NET, les exceptions levées lors de l’exécution de tâches sont encapsulées dans AggregateException . Lorsque vous utilisez async / wait, la structure effectue un dépliage sous le capot afin de lever l’exception originale à la place de l’expert encapsuleur AggregateException.

Outre que le code BCL doit renvoyer des exceptions existantes lors du décompression automatique, il existe des cas dans lesquels vous ne pouvez pas utiliser async / wait et que vous devez par conséquent décompresser et rediffuser manuellement. Pour un exemple pratique, consultez les utilisations internes de TaskExtensions.WaitAndUnwrapException dans l’implémentation AsyncContext dans l’excellent projet AsyncEx de Stephen Clearys.

Vous souhaiterez peut-être consigner l’exception, la consigner, puis la renvoyer sous la forme “throw ex” vers la couche supérieure. Vous voudrez peut-être aussi vous connecter à ce calque, mais le calque pour lequel vous avez une exception n’a peut-être rien à voir, ou vous n’aurez peut-être pas besoin des détails de l’exception puisque vous l’avez déjà enregistrée.

Disons que vous avez:

 Public int GetAllCustomers() { try { return data.GetAllCustomers() } catch() { Logger.log("Error calling GetAllCustomers"); } } //data Public static int GetAllCustomers() { try { //db operations... } catch(Exception ex) { Logger.log(ex.Stacktrace); throw ex; } }