Estou chamando, através da reflexão, de um método que pode causar uma exceção. Como posso passar a exceção para o meu interlocutor sem que o reflexo do wrapper o rodeie?
Estou repetindo a InnerException, mas isso destrói o rastreamento de pilha.
Código de exemplo:
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;
}
}
Respostas:
No .NET 4.5 , agora existe a
ExceptionDispatchInfo
classe.Isso permite capturar uma exceção e lançá-la novamente sem alterar o rastreamento de pilha:
Isso funciona em qualquer exceção, não apenas
AggregateException
.Foi introduzido devido ao
await
recurso de linguagem C #, que desvenda as exceções internas dasAggregateException
instâncias para tornar os recursos de linguagem assíncrona mais parecidos com os recursos de linguagem síncrona.fonte
throw;
após a linha .Throw (), porque o compilador não saberá que .Throw () sempre lança uma exceção.throw;
nunca será chamado como resultado, mas pelo menos o compilador não reclamará se o seu método exigir um objeto de retorno ou for uma função assíncrona.throw;
. Se você usarthrow ex.InnerException;
o rastreamento de pilha, será reinicializado no ponto em que será novamente reproduzido.ExceptionDispatchInfo.Capture(ex.InnerException ?? ex).Throw();
Ele é possível preservar o rastreamento de pilha antes rethrowing sem reflexão:
Isso desperdiça muitos ciclos em comparação com as chamadas
InternalPreserveStackTrace
via delegado em cache, mas tem a vantagem de depender apenas da funcionalidade pública. Aqui estão alguns padrões de uso comuns para funções de preservação de rastreamento de pilha:fonte
InternalPreserveStackTrace
(cerca de 6% mais lento com 10.000 iterações). Acessando campos diretamente pela reflexão é cerca de 2,5% mais rápido do que invocandoInternalPreserveStackTrace
e.Data
dicionário com uma string ou uma chave de objeto exclusiva (static readonly object myExceptionDataKey = new object ()
mas não faça isso se precisar serializar exceções em qualquer lugar). Evite modificare.Message
, porque você pode ter código em algum lugar que analisee.Message
. A análisee.Message
é ruim, mas pode não haver outra opção, por exemplo, se você precisar usar uma biblioteca de terceiros com práticas inadequadas de exceção.Eu acho que sua melhor aposta seria colocar isso no seu bloco de captura:
E depois extraia a inerexceção mais tarde.
fonte
Ninguém explicou a diferença entre
ExceptionDispatchInfo.Capture( ex ).Throw()
e uma planíciethrow
, então aqui está.A maneira completa de reconfigurar uma exceção capturada é usar
ExceptionDispatchInfo.Capture( ex ).Throw()
(disponível apenas no .Net 4.5).Abaixo há os casos necessários para testar isso:
1
2)
3)
4)
O caso 1 e o caso 2 fornecerão um rastreamento de pilha em que o número da linha do código-fonte do
CallingMethod
método é o número dathrow new Exception( "TEST" )
linha.No entanto, o caso 3 fornecerá um rastreamento de pilha em que o número da linha do código-fonte do
CallingMethod
método é o número da linha dathrow
chamada. Isso significa que, se athrow new Exception( "TEST" )
linha estiver cercada por outras operações, você não terá idéia de qual número de linha a exceção foi realmente lançada.O caso 4 é semelhante ao caso 2, porque o número da linha da exceção original é preservado, mas não é uma releitura real, pois altera o tipo da exceção original.
fonte
Chame o método de extensão em sua exceção antes de executá-lo, pois ele preservará o rastreamento de pilha original.
fonte
Action<Exception>
? Aqui usar método estáticoAinda mais reflexão ...
Lembre-se de que isso pode ocorrer a qualquer momento, pois os campos particulares não fazem parte da API. Veja mais discussões sobre o Mono bugzilla .
fonte
InternalPreserveStackTrace
método seria melhor, já que ele faz a mesma coisa e é menos provável de mudança no futuro ...Primeiro: não perca o TargetInvocationException - são informações valiosas quando você deseja depurar as coisas.
Segundo: Embrulhe o TIE como InnerException em seu próprio tipo de exceção e coloque uma propriedade OriginalException vinculada ao que você precisa (e mantenha intacta a pilha de chamadas inteira).
Terceiro: deixe o TIE sair do seu método.
fonte
Gente, você é legal .. Eu vou ser um necromante em breve.
fonte
.Invoke()
.Outro código de exemplo que usa serialização / desserialização de exceção. Não requer que o tipo de exceção real seja serializável. Também usa apenas métodos públicos / protegidos.
fonte
Com base na resposta de Paul Turners, fiz um método de extensão
o
return ex
ist nunca foi alcançado, mas a vantagem é que eu posso usarthrow ex.Capture()
como um liner para que o compilador não apresentenot all code paths return a value
erros.fonte