Evitar ao máximo o uso de rethrow de uma Exception, pois isto faz com que esta perca seu antigo callstack, ocultando o problema que originou a exceção.
Ruim:
try
{
// Do stuff that might throw an exception
}
catch (Exception e) {
throw e; // This destroys the stack trace information!
}
Bom:
try
{
// Code that may throw exceptions
}
catch (Exception ex)
{
// Adds error logging here
throw; // This preserves the stack trace
}
Sempre que possível realizar o tratamento de exceções especificas. Evitando algo como:
try
{
// Do stuff that might throw an exception
} catch(Exception) {}
- Sempre crie exceptions customizadas para contextualizar os erros de seu sistema;
- Utilize uma exception base para seu sistema, assim você pode separar as exceptions de seu sistema e exceptions inesperadas;
- Utilize um código de erro para identificar cada uma de suas exceptions. Isso faz com que a identificação de cada erro seja muito mais simples para o usuário de seu código.
Abaixo você pode encontrar alguns exemplos de boas exceptions e algumas não tão boas assim.
Ruim
public class InvalidUserException : Exception
{
public InvalidUserException(string message) : base(message)
{
}
}
Boa
public enum ErrorCode
{
UserNotFound = 1,
InvalidCnpj = 2
}
public abstract class BaseException : Exception
{
public BaseException(ErrorCode errorCode) : base(GetMessage(errorCode))
{
this.ErrorCode = errorCode;
}
public ErrorCode ErrorCode { get; }
private static string GetMessage(ErrorCode errorCode)
{
// Return message for the exception based on the error code
// Gets the message from a resource file
return ErrorMessages.ResourceManager.GetString(errorCode.ToString());
}
}
public class InvalidCnpjException : BaseException
{
public InvalidCnpjException() : base(ErrorCode.InvalidCnpj)
{
}
}
É muito importante que seu software seja capaz de apresentar feedbacks para os erros causados pelas ações de seu usuário. Uma forma de fazer isto é utilizando exceções para informá-lo de erros de entrada ou fluxo da aplicação. Porém, sua aplicação também deve ser capaz de se recuperar de erros não mapeados. Por este motivo, é muito importante que seu tratamento de exceções seja:
- Centralizado;
- Enxuto;
- Capaz de separar erros mapeados pela sua aplicação de erros não mapeados;
- Capaz de armazenar detalhes sobre erros não mapeados que ocorrerem na execução da aplicação;
Abaixo pode-se ver um exemplo de um bom tratamento de exceções e de um tratamento não tão bom:
Ruim
try
{
// Do some action
}
catch (Exception ex)
{
ShowDialog(ex.Message);
}
Aceitável, porém com muita repetição
try
{
// Do some action
}
catch (InvalidUserException ex)
{
ShowDialog(101, ex.Message);
}
catch (InvalidActionException ex)
{
ShowDialog(102, ex.Message);
}
catch (UserNotFoundException ex)
{
ShowDialog(299, ex.Message);
}
catch (Exception ex)
{
// Log exception
this.logger.Log(ex);
ShowDialog(500, "Unexpected error happened");
}
Bom
try
{
// Do some action
}
catch (BaseException ex)
{
ShowDialog(ex.ErrorCode, ex.Message);
}
catch (Exception ex)
{
this.logger.Log(ex);
ShowDialog(ErrorCode.UnknownError, "Unexpected error happened");
}