Na verdade, existem algumas situações em que a throw
declaração não preserva as informações do StackTrace. Por exemplo, no código abaixo:
try
{
int i = 0;
int j = 12 / i; // Line 47
int k = j + 1;
}
catch
{
// do something
// ...
throw; // Line 54
}
O StackTrace indicará que a linha 54 gerou a exceção, embora tenha sido gerada na linha 47.
Unhandled Exception: System.DivideByZeroException: Attempted to divide by zero.
at Program.WithThrowIncomplete() in Program.cs:line 54
at Program.Main(String[] args) in Program.cs:line 106
Em situações como a descrita acima, existem duas opções para preservar o StackTrace original:
Chamando o Exception.InternalPreserveStackTrace
Como é um método privado, ele deve ser chamado usando reflexão:
private static void PreserveStackTrace(Exception exception)
{
MethodInfo preserveStackTrace = typeof(Exception).GetMethod("InternalPreserveStackTrace",
BindingFlags.Instance | BindingFlags.NonPublic);
preserveStackTrace.Invoke(exception, null);
}
Eu tenho uma desvantagem de depender de um método privado para preservar as informações do StackTrace. Pode ser alterado em versões futuras do .NET Framework. O exemplo de código acima e a solução proposta abaixo foram extraídos do blog Fabrice MARGUERIE .
Chamando Exception.SetObjectData
A técnica abaixo foi sugerida por Anton Tykhyy como resposta ao In C #, como posso reconfigurar o InnerException sem perder a pergunta de rastreamento de pilha .
static void PreserveStackTrace (Exception e)
{
var ctx = new StreamingContext (StreamingContextStates.CrossAppDomain) ;
var mgr = new ObjectManager (null, ctx) ;
var si = new SerializationInfo (e.GetType (), new FormatterConverter ()) ;
e.GetObjectData (si, ctx) ;
mgr.RegisterObject (e, 1, si) ; // prepare for SetObjectData
mgr.DoFixups () ; // ObjectManager calls SetObjectData
// voila, e is unmodified save for _remoteStackTraceString
}
Embora tenha a vantagem de confiar apenas em métodos públicos, também depende do construtor de exceções a seguir (que algumas exceções desenvolvidas por terceiros não implementam):
protected Exception(
SerializationInfo info,
StreamingContext context
)
Na minha situação, tive que escolher a primeira abordagem, porque as exceções levantadas por uma biblioteca de terceiros que eu estava usando não implementaram esse construtor.