J'appelle, par réflexion, une méthode qui peut provoquer une exception. Comment puis-je transmettre l'exception à mon appelant sans que la réflexion du wrapper ne l'entoure?
Je rejette l'InnerException, mais cela détruit la trace de la pile.
Exemple de code:
public void test1()
{
// Throw an exception for testing purposes
throw new ArgumentException("test1");
}
void test2()
{
try
{
MethodInfo mi = typeof(Program).GetMethod("test1");
mi.Invoke(this, null);
}
catch (TargetInvocationException tiex)
{
// Throw the new exception
throw tiex.InnerException;
}
}
Réponses:
Dans .NET 4.5, il y a maintenant la
ExceptionDispatchInfo
classe.Cela vous permet de capturer une exception et de la renvoyer sans modifier la trace de pile:
Cela fonctionne sur n'importe quelle exception, pas seulement
AggregateException
.Il a été introduit en raison de la
await
fonctionnalité du langage C #, qui déballe les exceptions internes desAggregateException
instances afin de rendre les fonctionnalités du langage asynchrone plus semblables aux fonctionnalités du langage synchrone.la source
throw;
après la ligne .Throw (), car le compilateur ne saura pas que .Throw () lève toujours une exception.throw;
ne sera jamais appelé en conséquence, mais au moins le compilateur ne se plaindra pas si votre méthode nécessite un objet de retour ou est une fonction asynchrone.throw;
. Si vous utilisezthrow ex.InnerException;
le stack-trace est réinitialisé au point où il est relancé.ExceptionDispatchInfo.Capture(ex.InnerException ?? ex).Throw();
Il est possible de conserver la trace de la pile avant de relancer sans réflexion:
Cela gaspille beaucoup de cycles par rapport à un appel
InternalPreserveStackTrace
via un délégué mis en cache, mais a l'avantage de ne s'appuyer que sur des fonctionnalités publiques. Voici quelques modèles d'utilisation courants pour les fonctions de conservation de trace de pile:la source
InternalPreserveStackTrace
(environ 6% plus lent avec 10000 itérations). L'accès direct aux champs par réflexion est environ 2,5% plus rapide que l'invocationInternalPreserveStackTrace
e.Data
dictionnaire avec une chaîne ou une clé d'objet unique (static readonly object myExceptionDataKey = new object ()
, mais ne le faites pas si vous devez sérialiser des exceptions n'importe où). Évitez de modifiere.Message
, car vous pourriez avoir du code quelque part qui analysee.Message
. L'analysee.Message
est mauvaise, mais il n'y a peut-être pas d'autre choix, par exemple si vous devez utiliser une bibliothèque tierce avec de mauvaises pratiques d'exception.Je pense que votre meilleur pari serait de simplement mettre cela dans votre bloc de capture:
Et puis extrayez plus tard l'exception interne.
la source
Personne n'a expliqué la différence entre
ExceptionDispatchInfo.Capture( ex ).Throw()
et une plainethrow
, alors la voici.La manière complète de renvoyer une exception interceptée consiste à utiliser
ExceptionDispatchInfo.Capture( ex ).Throw()
(uniquement disponible à partir de .Net 4.5).Vous trouverez ci-dessous les cas nécessaires pour tester cela:
1.
2.
3.
4.
Le cas 1 et le cas 2 vous donneront une trace de pile où le numéro de ligne de code source pour la
CallingMethod
méthode est le numéro dethrow new Exception( "TEST" )
ligne de la ligne.Cependant, le cas 3 vous donnera une trace de pile où le numéro de ligne de code source pour la
CallingMethod
méthode est le numéro de ligne de l'throw
appel. Cela signifie que si lathrow new Exception( "TEST" )
ligne est entourée d'autres opérations, vous ne savez pas à quel numéro de ligne l'exception a été levée.Le cas 4 est similaire au cas 2 car le numéro de ligne de l'exception d'origine est conservé, mais il ne s'agit pas d'un véritable renversement car il modifie le type de l'exception d'origine.
la source
Appelez la méthode d'extension sur votre exception avant de la lancer, elle conservera la trace de pile d'origine.
la source
Action<Exception>
? Ici, utilisez la méthode statiqueEncore plus de réflexion ...
Gardez à l'esprit que cela peut se casser à tout moment, car les champs privés ne font pas partie de l'API. Voir plus de discussion sur Mono bugzilla .
la source
InternalPreserveStackTrace
serait préférable d' appeler la méthode interne , car elle fait la même chose et est moins susceptible de changer à l'avenir ...Premièrement: ne perdez pas l'exception TargetInvocationException - ce sont des informations précieuses lorsque vous voudrez déboguer des choses.
Deuxièmement: Enveloppez le TIE en tant qu'InnerException dans votre propre type d'exception et mettez une propriété OriginalException qui lie à ce dont vous avez besoin (et conservez l'intégralité de la pile d'appels intacte).
Troisièmement: laissez la bulle TIE sortir de votre méthode.
la source
Les gars, vous êtes cool .. je vais bientôt devenir nécromancien.
la source
.Invoke()
.Un autre exemple de code qui utilise la sérialisation / désérialisation d'exception. Il ne nécessite pas que le type d'exception réel soit sérialisable. De plus, il utilise uniquement des méthodes publiques / protégées.
la source
Sur la base de la réponse de Paul Turners, j'ai fait une méthode d'extension
les
return ex
ist n'a jamais atteint, mais l'avantage est que je peux l'utiliserthrow ex.Capture()
comme une ligne pour que le compilateur ne génère pas d'not all code paths return a value
erreur.la source