Eu estou vendo o artigo C # - Data Transfer Object em DTOs serializáveis.
O artigo inclui este pedaço de código:
public static string SerializeDTO(DTO dto) {
try {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
catch(Exception ex) {
throw ex;
}
}
O restante do artigo parece sensato e razoável (para um noob), mas esse try-catch-throw lança uma WtfException ... Isso não é exatamente equivalente a não lidar com exceções?
Ergo:
public static string SerializeDTO(DTO dto) {
XmlSerializer xmlSer = new XmlSerializer(dto.GetType());
StringWriter sWriter = new StringWriter();
xmlSer.Serialize(sWriter, dto);
return sWriter.ToString();
}
Ou estou perdendo algo fundamental sobre o tratamento de erros em c #? É praticamente o mesmo que Java (menos exceções verificadas), não é? ... Ou seja, ambos refinaram o C ++.
A questão do estouro de pilha A diferença entre relançar a captura sem parâmetros e não fazer nada? parece apoiar minha afirmação de que o try-catch-throw é um não-op.
EDITAR:
Apenas para resumir para quem encontrar este tópico no futuro ...
NÃO
try {
// Do stuff that might throw an exception
}
catch (Exception e) {
throw e; // This destroys the strack trace information!
}
As informações de rastreamento da pilha podem ser cruciais para identificar a causa raiz do problema!
FAZ
try {
// Do stuff that might throw an exception
}
catch (SqlException e) {
// Log it
if (e.ErrorCode != NO_ROW_ERROR) { // filter out NoDataFound.
// Do special cleanup, like maybe closing the "dirty" database connection.
throw; // This preserves the stack trace
}
}
catch (IOException e) {
// Log it
throw;
}
catch (Exception e) {
// Log it
throw new DAOException("Excrement occurred", e); // wrapped & chained exceptions (just like java).
}
finally {
// Normal clean goes here (like closing open files).
}
Capte as exceções mais específicas antes das menos específicas (assim como Java).
Referências:
fonte
Respostas:
Primeiro; a maneira como o código no artigo faz isso é ruim.
throw ex
redefinirá a pilha de chamadas na exceção para o ponto em que esta instrução throw está; perdendo as informações sobre onde a exceção realmente foi criada.Segundo, se você capturar e jogar novamente dessa maneira, não vejo valor agregado, o exemplo de código acima seria tão bom (ou,
throw ex
melhor ainda) sem o try-catch.No entanto, há casos em que você pode querer capturar e relançar uma exceção. O log pode ser um deles:
fonte
ex
objeto, não será necessário instanciar o objeto.throw ex
não reinicia o stacktrace.Não faça isso,
Você perderá as informações de rastreamento da pilha ...
Qualquer um,
OU
Uma das razões pelas quais você pode querer relançar novamente é se estiver lidando com exceções diferentes, por exemplo,
fonte
O C # (antes do C # 6) não suporta "exceções filtradas" do CIL, o que o VB faz; portanto, no C # 1-5, uma razão para lançar uma exceção é que você não possui informações suficientes no momento da captura () para determinar se você realmente deseja capturar a exceção.
Por exemplo, no VB você pode fazer
... que não trataria MyExceptions com diferentes valores de ErrorCode. No C # anterior à v6, você precisaria capturar e lançar novamente a MyException se o ErrorCode não fosse 123:
Desde o C # 6.0, você pode filtrar como no VB:
fonte
Meu principal motivo para ter código como:
é para que eu possa ter um ponto de interrupção na captura, que tenha um objeto de exceção instanciada. Eu faço muito isso durante o desenvolvimento / depuração. Obviamente, o compilador me dá um aviso sobre todos os e não utilizados e, idealmente, eles devem ser removidos antes da compilação do release.
Eles são bons durante a depuração embora.
fonte
#if DEBUG
em torno de ambos ostry {
e} catch () {...}
é um pouco bagunçado e, francamente, me deixa enjoado ... O pré-processador, em geral, não é meu amigo.Um motivo válido para relançar exceções pode ser o fato de você desejar adicionar informações à exceção ou talvez agrupar a exceção original em uma criação sua:
fonte
Não exatamente, não é o mesmo. Redefine o rastreamento de pilha da exceção. Embora eu concorde que isso provavelmente seja um erro e, portanto, um exemplo de código incorreto.
fonte
Você não quer jogar ex - pois isso perderá a pilha de chamadas. Consulte Tratamento de exceções (MSDN).
E sim, o try ... catch não está fazendo nada útil (além de perder a pilha de chamadas - então é realmente pior - a menos que, por algum motivo, você não queira expor essas informações).
fonte
Um ponto que as pessoas não mencionaram é que, embora as linguagens .NET não façam uma distinção adequada, a questão de saber se alguém deve agir quando ocorre uma exceção e se deve resolver , são realmente perguntas distintas. Existem muitos casos em que uma ação deve ser tomada com base em exceções que não temos esperança de resolver, e em alguns casos tudo o que é necessário para "resolver" uma exceção é relaxar a pilha até um determinado ponto - nenhuma ação adicional é necessária. .
Por causa do senso comum de que só se deve "capturar" as coisas que se pode "manipular", muito código que deve agir quando ocorrem exceções, não. Por exemplo, muitos códigos adquirem um bloqueio, colocam o objeto guardado "temporariamente" em um estado que viole seus invariantes, em seguida, colocam o objeto em um estado legítimo e liberam o bloqueio antes que mais alguém possa ver o objeto. Se ocorrer uma exceção enquanto o objeto estiver em um estado perigosamente inválido, a prática comum é liberar o bloqueio com o objeto ainda nesse estado. Um padrão muito melhor seria ter uma exceção que ocorra enquanto o objeto estiver em uma condição "perigosa" invalidará expressamente o bloqueio, para que qualquer tentativa futura de adquiri-lo falhe imediatamente.
Na maioria das linguagens .NET, a única maneira de o código executar uma ação com base em uma exceção é
catch
fazê - lo (mesmo sabendo que não resolverá a exceção), execute a ação em questão e depois rethrow
). Outra abordagem possível, se o código não se importa com a exceção lançada, é usar umok
sinalizador com umtry/finally
bloco; defina ook
sinalizador comofalse
antes do bloco etrue
antes que o bloco saia e antes de qualquer umreturn
que esteja dentro do bloco. Em seguida,finally
assuma que, seok
não estiver definido, uma exceção deve ter ocorrido. Essa abordagem é semanticamente melhor que acatch
/throw
, mas é feia e é menos sustentável do que deveria.fonte
Isso pode ser útil quando sua programação funciona para uma biblioteca ou dll.
Essa estrutura de relançamento pode ser usada para redefinir propositalmente a pilha de chamadas para que, em vez de ver a exceção lançada de uma função individual dentro da função, você obtenha a exceção da própria função.
Eu acho que isso é usado apenas para que as exceções lançadas sejam mais limpas e não entrem nas "raízes" da biblioteca.
fonte
Um motivo possível para capturar é impedir que filtros de exceção mais profundos da pilha sejam filtrados ( link antigo aleatório ). Mas é claro que, se essa era a intenção, haveria um comentário dizendo isso.
fonte
Depende do que você está fazendo no bloco catch e se deseja passar o erro para o código de chamada ou não.
Pode-se dizer
Catch io.FileNotFoundExeption ex
e usar um caminho de arquivo alternativo ou algo parecido, mas ainda assim ativar o erro.Também fazer em
Throw
vez deThrow Ex
permite manter o rastreamento completo da pilha. Throw ex reinicia o rastreamento de pilha da instrução throw (espero que faça sentido).fonte
Enquanto muitas das outras respostas fornecem bons exemplos de por que você pode querer capturar uma exceção de relançamento, ninguém parece ter mencionado um cenário 'finalmente'.
Um exemplo disso é onde você tem um método no qual define o cursor (por exemplo, para um cursor de espera), o método possui vários pontos de saída (por exemplo, se () retorna;) e deseja garantir que o cursor seja redefinido no fim do método.
Para fazer isso, você pode agrupar todo o código em uma tentativa / captura / finalmente. Finalmente, defina o cursor de volta ao cursor direito. Para que você não esconda nenhuma exceção válida, repita-a novamente.
fonte
catch
uma parte obrigatória dotry...finally
histórico ou desempenha um papel funcional neste exemplo? - Apenas verifiquei duas vezes e posso usartry {} finally {}
sem o bloco de captura.No exemplo do código que você postou, de fato, não faz sentido capturar a exceção, pois não há nada feito na captura, apenas refazer a ação; na verdade, causa mais mal do que bem, pois a pilha de chamadas é perdida. .
Você, no entanto, capturaria uma exceção para fazer alguma lógica (por exemplo, fechar a conexão sql do bloqueio de arquivo ou apenas alguns registros) no caso de uma exceção, devolvê-la ao código de chamada para lidar. Isso seria mais comum em uma camada de negócios do que no código de front-end, pois você pode querer que o codificador que implementa sua camada de negócios lide com a exceção.
Para reiterar o ponto NÃO há como capturar a exceção no exemplo que você postou. NÃO faça assim!
fonte
Desculpe, mas muitos exemplos de "design aprimorado" ainda cheiram horrivelmente ou podem ser extremamente enganadores. Tendo tentado {} catch {log; jogar} é totalmente inútil. O registro de exceção deve ser feito em um local central dentro do aplicativo. de qualquer maneira, as exceções aumentam o rastreamento de pilha, por que não registrá-las em algum lugar próximo às bordas do sistema?
Deve-se tomar cuidado ao serializar seu contexto (por exemplo, DTO em um exemplo) apenas na mensagem de log. Ele pode facilmente conter informações confidenciais que talvez você não queira alcançar nas mãos de todas as pessoas que podem acessar os arquivos de log. E se você não adicionar nenhuma informação nova à exceção, realmente não vejo o ponto de agrupar as exceções. O bom e velho Java tem algum argumento para isso, exige que o chamador saiba que tipo de exceções se deve esperar ao chamar o código. Como você não possui isso no .NET, o agrupamento não é bom em pelo menos 80% dos casos que eu já vi.
fonte
Além do que os outros disseram, veja minha resposta a uma pergunta relacionada que mostra que capturar e relançar não é um não-op (está no VB, mas parte do código pode ser invocada em C # pelo VB).
fonte
A maioria das respostas fala sobre o cenário catch-log-rehrow.
Em vez de escrevê-lo em seu código, considere usar AOP, em particular Postsharp.Diagnostic.Toolkit com OnExceptionOptions IncludeParameterValue e IncludeThisArgument
fonte
A reconfiguração de exceções via
throw
é útil quando você não possui um código específico para lidar com exceções atuais ou nos casos em que você tem uma lógica para lidar com casos de erro específicos, mas deseja pular todos os outros.Exemplo:
No entanto, há também outra maneira de fazer isso, usando cláusulas condicionais em blocos catch:
fonte