terça-feira, 14 de abril de 2009

Realizando uma análise mais apurada de exceções

Você já parou para analisar como tem tratado exceções nas suas aplicações?
Eu já tive vários problemas em projetos cujo tratamento de exceções foi ignorado ou pior foi mal feito, isso mesmo, quando não tratamos uma exceção de forma adequada geralmente os resultados são piores do que não tratando.

Mas isso não significa que você nunca tenha que aprender a fazer um eficiente tratamento de exceção.

Quando uma exceção é lançada ela contém várias informações importantes que lhe servem como base para solucionar o problema.
O tipo da exceção já pode lhe fornecer algumas dicas da causa do problema, por exemplo:

ArgumentNullException: Essa exceção é disparada quando um método recebe um argumento com um valor nulo.
DivideByZeroException: Essa exceção é disparada quando é realizada uma divisão por zero.
InvalidCastException: Exceção disparada quando não foi possível realizar a conversão do objeto.

Para quem trabalha com aplicações em camadas, o tipo da exceção pode especificar exatamente em qual camada foi disparada a exceção, por exemplo: System.Data.SqlCliente.SqlException só deve ser disparada na camada de acesso a dados.

A mensagem contida na exceção também fornece informações importantes para a resolução do problema, como exemplo, a mensagem “Login failed for user xyz” na exceção System.Data.SqlCliente.SqlException indica que não foi possível conectar no banco de dados com o usuário xyz. Já a mensagem “Value cannot be null. Parameter name: myObject” contida na exceção ArgumentNullException indica que ao chamar um método que contém o argumento myObject esta sendo passado valor nulo.

Por enquanto tudo o que foi escrito parece óbvio, mas acredite, perdi a conta de quantas vezes me deparei com programador sofrendo por horas para tentar resolver um problema e quando perguntava qual era a mensagem de erro o “campeão” não sabia dizer.

Outro recurso muito útil que uma exceção fornece para lhe ajudar a resolver o problema é o StackTrace. O StackTrace contém o rastro da exceção dentro da aplicação desde o momento em que foi gerada até o ponto em que foi tratada, exemplo:

************** Exception Text **************
System.ArgumentNullException: Value cannot be null.
Parameter name: view
at Cronos.CreationDbFiles.Application.ConnectToServerPresenter..ctor(IConnectToServerView view) in C:\Projetos\Cronos.CreationDatabaseFiles\src\Cronos.CreationDbFiles\Application\ConnectToServerPresenter.cs:line 22
at Cronos.CreationDbFiles.frmConnectServer.frmConnectServer_Shown(Object sender, EventArgs e) in C:\Projetos\Cronos.CreationDatabaseFiles\src\Cronos.CreationDbFiles\frmConnectServer.cs:line 133
at System.Windows.Forms.Form.OnShown(EventArgs e)
at System.Windows.Forms.Form.CallShownEvent()
at System.Windows.Forms.Control.InvokeMarshaledCallbackDo(ThreadMethodEntry tme)
at System.Windows.Forms.Control.InvokeMarshaledCallbackHelper(Object obj)
at System.Threading.ExecutionContext.runTryCode(Object userData)
at System.Runtime.CompilerServices.RuntimeHelpers.ExecuteCodeWithGuaranteedCleanup(TryCode code, CleanupCode backoutCode, Object userData)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Windows.Forms.Control.InvokeMarshaledCallback(ThreadMethodEntry tme)
at System.Windows.Forms.Control.InvokeMarshaledCallbacks()

No StackTrace acima ele mostra claramente em que classe, método, e linha foi disparado o erro, neste caso em específico ocorreu na linha 22 do arquivo ConnectToServerPresenter.cs. Repare que o StackTrance contém informações inclusive das classes do .Net Framework como por exemplo at System.Windows.Forms.Form.OnShown(EventArgs e). Isso é muito útil para identificar até que ponto a exceção foi trafegada dentro das classes da aplicação ou do .Net Framework.

Por tanto, antes de ficar tentando debugar a aplicação para identificar o ponto onde ocorreu o erro procure verificar o StackTrace ele pode te ajudar com essa tarefa.

Mas precisamos entender que algumas informações só são úteis para equipe de desenvolvimento / suporte não para o usuário. Exibir mensagens de erros do tipo “Login failed for user xyz” ou “Attempted to divide by zero” em uma tela amarela, não ajuda ao usuário a identificar o que ele deve fazer para resolver esse problema.

Bom, é ai que começa a surgir os grandes problemas, quando o “campeão” fica sabendo que não é agradável para o usuário se deparar com uma tela amarela contendo mensagens que não fazem sentido algum, o que ele faz? Ele escreve um código conforme abaixo:

try
{
// minha rotina
}
catch(Exception)
{
MessageBox.Show(“Não foi possível realizar a operação, favor contactar o administrador do sistema”);
}

Ou pior:

try
{
// minha rotina
}
catch
{

}

No primeiro exemplo nós continuamos exibindo uma mensagem que não é útil ao usuário, a mensagem não contém informações que ajude a resolver o problema. E se o usuário tiver a sorte de encontrar o administrador do sistema, que informação estamos dando a ele para resolver o problema?

No segundo exemplo o problema esta resolvido..... Nunca teremos o problema, a aplicação nunca dará erro. Pelo menos é isso que o programador deve estar achando quando escreve um código desses.

Não podemos ignorar a exceção devido a importância das informações que nela esta contida, mas também não podemos exibir informações ao usuário que não tem relevância ao negócio.

Abaixo contém alguns links úteis contendo melhores práticas para tratamento de exceções:

Exception Handling Best Practices in .NET


Design Guidelines for Exceptions


User-Friendly Exception Handling


Exceptions and Performance in .NET

Nenhum comentário: