Usando múltiplas capturas de Exceptions em C#

O que são Exceptions

Segundo a documentação da Microsoft:

Uma exceção é qualquer condição de erro ou comportamento inesperado encontrado por um programa em execução. Toda exceção é um objeto herdado da classe System.Exception e é lançada de uma área do código em que ocorreu um problema.

Tabela de exceções mais comuns e suas hierarquias

TIPO BASE
Exception Object
SystemException Exception
IndexOutOfRangeException SystemException
NullReferenceException SystemException
AccessViolationException SystemException
InvalidOperationException SystemException
ArgumentException SystemException
ArgumentNullException ArgumentException
ArgumentOutOfRangeException ArgumentException
ExternalException SystemException
COMException ExternalException
SEHException ExternalException

Como mostrados na tabela, todas as exceções tem sua base na classe Exception que por sua vez herda da Object.

A classe Exception, e por herança todas as outras exceptions, tem as propriedades a seguir, que ajudam a facilitar o entendimento de uma exceção.

Nome da Propriedade Descrição
Data Um IDictionary que contém dados arbitrários em pares chave-valor.
HelpLink Pode conter uma URL (ou URN) para um arquivo de ajuda que fornece informações abrangentes sobre a causa de uma exceção.
InnerException Essa propriedade pode ser usada para criar e manter uma série de exceções durante o tratamento de exceção.
Message Fornece detalhes sobre a causa de uma exceção.
Source Obtém ou define o nome do aplicativo ou objeto que causa o erro.
StackTrace Contém um rastreamento de pilha que pode ser usado para determinar onde um erro ocorreu.

Como fazer a captura de uma exceção?

Para realizar a captura de uma exceção basta colocar o seu código dentro de um bloco de TRY – CATCH

No exemplo abaixo, forcei uma divisão por zero e mandei imprimir a mensagem contida na exception:

using System;					
public class Program
{
	public static void Main()
	{
		try
		{
			int a = 2, b = 0;
			float z = a / b;
			Console.WriteLine("His will not executed");
		}
		catch(Exception e)
		{
			Console.WriteLine("Exception: " + e.Message);
		}
		finally
		{
			Console.WriteLine("This will always be executed");
		}
	}
}

Neste exemplo acima a saída vai ser:

Exception: Attempted to divide by zero.
This will always be executed

Trabalhando com múltiplas exceções.

Antes da versão 6 do C# uma maneira de lidar com múltiplas exceções é verificando dentro do bloco catch qual o tipo gerado.

Segue um exemplo deste modelo

using System;
namespace multipleCatch
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CatchException sampleException = new CatchException();
            sampleException.BeforeCSharp6("FormatException");
            sampleException.BeforeCSharp6("OverflowException");
            sampleException.BeforeCSharp6("Exception");
        }
    }
    class CatchException
    {
        public void BeforeCSharp6(string ExceptionType)
        {
            try
            {
                switch (ExceptionType)
                {
                    case "FormatException":
                        throw new FormatException();
                    case "OverflowException":
                        throw new OverflowException();
                    default:
                        throw new Exception();
                }
            }
            catch (Exception ex)
            {
                if (ex is FormatException || ex is OverflowException)
                {
                    Console.WriteLine("This is a FormatException or OverflowException ");
                    return;
                }
                Console.WriteLine("This is NOT a FormatException or OverflowException ");
            }
            return;
        }
    }
}

Neste exemplo acima a saída vai ser:

This is a FormatException or OverflowException 
This is a FormatException or OverflowException 
This is NOT a FormatException or OverflowException

Um dos novos recursos do C# 6 foi o de filtros de exceção. Eles permitem que você tenha ainda mais controle sobre seus blocos de captura e adapte ainda mais a forma como você lida com exceções específicas. Isso pode ajudá-lo a ajustar exatamente como você lida com exceções e quais você deseja capturar.

Segue um exemplo deste modelo

using System;
namespace multipleCatch
{
    class Program
    {
        static void Main(string[] args)
        {
            CatchException sampleException = new CatchException();
            sampleException.AfterCSharp6("FormatException");
            sampleException.AfterCSharp6("OverflowException");
            sampleException.AfterCSharp6("Exception");
		}
    }

    class CatchException
    {
        public void AfterCSharp6(string ExceptionType)
        {
            try
            {
                switch (ExceptionType)
                {
                    case "FormatException":
                        throw new FormatException();
                    case "OverflowException":
                        throw new OverflowException();
                    default:
                        throw new Exception();
                }
            }
            catch (Exception e) when (e is OverflowException || e is FormatException)
            {
                Console.WriteLine("This is a FormatException or OverflowException ");
            }
            catch (Exception e)
            {
                Console.WriteLine("This is NOT a FormatException or OverflowException ");
            }
        }
    }
}

Neste exemplo acima a saída vai ser:

This is a FormatException or OverflowException
This is a FormatException or OverflowException
This is NOT a FormatException or OverflowException

Custom Exception

Segundo a documentação da Microsoft:

Se nenhuma das exceções predefinidas atender às suas necessidades, será possível criar suas próprias classes de exceção derivando da classe Exception.
Ao criar suas próprias exceções, encerre o nome de classe de exceção definida pelo usuário com a palavra “Exception” e implemente os três construtores comuns, como mostrado no exemplo a seguir.

    public class CustomException : Exception
    {
        public CustomException() { }

        public CustomException(string message) : base(message) { }

        public CustomException(string message, Exception inner) : base(message, inner) { }

    }

Com a sua CustomException criada podemos fazer a captura de exceções da seguinte maneira

using System;
namespace multipleCatch
{
    class Program
    {
        static void Main(string[] args)
        {
            CatchException sampleException = new CatchException();
            sampleException.CustomExceptionSample("CustomException");
            sampleException.CustomExceptionSample("Exception");
        }
    }
    class CatchException
    {
        public void CustomExceptionSample(string ExceptionType)
        {
            try
            {
                switch (ExceptionType)
                {
                    case "CustomException":
                        throw new CustomException();
                    default:
                        throw new Exception();
                }
            }
            catch (CustomException e)
            {
                Console.WriteLine("This is a CustomException");
            }
            catch (Exception e)
            {
                Console.WriteLine("This is NOT a CustomException");
            }
        }
    }
    public class CustomException : Exception
    {
        public CustomException() { }

        public CustomException(string message) : base(message) { }

        public CustomException(string message, Exception inner) : base(message, inner) { }
    }
}

Neste exemplo acima a saída vai ser:

This is a CustomException
This is NOT a CustomException

Organizando seu código

Existe muitas maneiras de organizar seu código para que a leitura a manutenção fique mais simples. Encontrei muita coisa na internet e acredito que neste ponto, cada desenvolvedor deva seguir da maneira que julga melhor.

Vários blocos de cacth

try
{
    // => my code
}
catch (FormatException e)
{
    ExceptionHandlerMethod(e)
}
catch (OverflowException e)
{
    ExceptionHandlerMethod(e)
}

Tratamento dentro do catch global

try
{
    // => my code
}
catch (Exception e)            
{                
    if (e is FormatException || e is OverflowException)
    {
        ExceptionHandlerMethod(e);
		return
    }
    throw;
}

Método para tratamento de exceções com vários blocos de catch

try
{
	// => my code
}
catch ( FormatException  e ) { ExceptionHandlerMethod(e); }
catch ( OverflowException e ) { ExceptionHandlerMethod(e); }
catch ( Exception e ) { ExceptionHandlerMethod(e); }

Usando o filter

try 
{
	// => my code
}
catch (Exception e) when (e is ArgumentNullException || e is FormatException)
{
	ExceptionHandlerMethod(e);
}
catch ( Exception e ) { ExceptionHandlerMethod(e); }

O tratamento de exceções então pode varia de acordo como projeto em que esta sendo aplicado.Cabe então ao desenvolvedor utilizar a melhor solução para cada caso.

Nota:
Uma vez que uma exceção é lançada, parte das informações que ela carrega é o rastreamento de pilha. O rastreamento de pilha é uma lista da hierarquia de chamada do método que começa com o método que lança a exceção e termina com o método que captura a exceção. Se uma exceção for restabelecida especificando-se a exceção na instrução throw, por exemplo usando “throw e;”, o rastreamento de pilha é reiniciado no método atual e a lista de chamadas de método entre o método original que lançou a exceção e o método atual é perdida. Para manter as informações de rastreio de pilha originais com exceção, use a instrução throw sem especificar a exceção como no exemplo a seguir.

        try 
        {
        	// => my code
        }
        catch (Exception e)
        {
        	ExceptionHandlerMethod(e);
            throw;
        }

Qualquer dúvida ou dicas, entre em contato: leandrolt@gmail.com

Referencias:
stackoverflow: catch-multiple-exceptions-at-once
microsoft: exception-class-and-properties
airbrake.io: exception-class-hierarchy
stackify: csharp-exception-handling-best-practices

Leave a Reply

Your email address will not be published. Required fields are marked *