C# - Diferença entre throw e throw ex


Apresentando as diferenças entre throw e throw ex no tratamento de exceções na linguagem c#.

Quando tratamos exceções na linguagem C# geralmente usamos o bloco try/catch/finally e podemos encontrar diversos exemplos que usam duas abordagens :

  1. throw
  2. throw ex
 try
 {
    //algum código
 }
 catch
 {
    throw;
 }
 try
 {
    //algum código
 }
 catch (Exception ex)
 {
     throw ex;
 }

Vamos entender o que acontece em cada abordagem:

1 - lançando um throw

Quando você lança apenas um throw está repassando a mesma exceção para frente e, dessa forma, outro trecho de código poderá capturar e saber o que vai fazer com a exceção original retendo todas as informações necessárias com o stack trace.

2 - lançando um throw ex

Quando você  lança um throw ex, você esta parando a exceção ali e após fazer alguma operação, você lança outra exceção a partir dali. Dessa forma a informação de onde veio a exceção original é perdida. Tudo se passa como se tivesse ocorrido uma nova exceção.

Agora vamos usar um exemploprático para você perceber como isso é importante e porque você deve dar preferência  usar a primeira opção.

Recursos Usados neste artigo :

Criando o projeto Console

Crie um projeto Console Application e inclua o seguinte código no arquivo Program.cs :

1 - lançando um throw

using System;
namespace CShp_Exceptions
{
    class Program
    {
        static void Main(string[] args)
        {
            Funcionario funci = new Funcionario();
            Console.WriteLine(funci.GetDetalhes());
            Console.Read();
        }
        class Funcionario
        {
            public String GetDetalhes()
            {
                try
                {
                    Departamento depto = new Departamento();
                    return depto.GetDepartmentos();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("---Registra Stack Trace para exceções----");
                    Console.WriteLine(ex.StackTrace.ToString());
                    Console.WriteLine("---Registra o nome do método na qual a exceção ocorreu---");
                    Console.WriteLine(ex.TargetSite.ToString());
                }
                return "";
            }
        }
        class Departamento
        {
            public String GetDepartmentos()
            {
                try
                {
                    DividirPorZero();
                    return "Método GetDepartmentos foi chamado";
                }
                catch(Exception ex) 
                {
                    throw;
                }
            }
            //estamos gerando uma exception 
            public void DividirPorZero()
            {
                throw new DivideByZeroException();
            }
        }
    }
}

Neste código estamos gerando uma exceção do tipo divisão por zero e usando um try/catch lançado apenas throw.

Veja o resultado obtido:

Observe que todos os métodos foram registrados na pilha; inclusive o método DividirPorZero() que causou a exceção.

2 - lançando um throw ex

using System;
namespace CShp_Exceptions
{
    class Program
    {
        static void Main(string[] args)
        {
            Funcionario funci = new Funcionario();
            Console.WriteLine(funci.GetDetalhes());
            Console.Read();
        }
        class Funcionario
        {
            public String GetDetalhes()
            {
                try
                {
                    Departamento depto = new Departamento();
                    return depto.GetDepartmentos();
                }
                catch (Exception ex)
                {
                    Console.WriteLine("---Registra Stack Trace para exceções----");
                    Console.WriteLine(ex.StackTrace.ToString());
                    Console.WriteLine("---Registra o nome do método na qual a exceção ocorreu---");
                    Console.WriteLine(ex.TargetSite.ToString());
                }
                return "";
            }
        }
        class Departamento
        {
            public String GetDepartmentos()
            {
                try
                {
                    DividirPorZero();
                    return "Método GetDepartmentos foi chamado";
                }
                catch(Exception ex) 
                {
                    throw ex;
                }
            }
            //estamos gerando uma exception 
            public void DividirPorZero()
            {
                throw new DivideByZeroException();
            }
        }
    }
}

Neste código apenas alteramos o bloco try/catch e agora estamos lançando um throw ex.

Veja o resultado:

Analisando a pilha exibida na figura acima note que apenas os métodos GetDepartamentos e GetDetalhes foram gravados na pilha.

O método DividirPorZero() que foi a causa da exceção não foi gravado na pilha.

Como usamos throw ex no método GetDepartamentos a pilha removeu o DividirPorZero() da pilha.

Concluimos assim que throw mantém a hierarquia de exceções no rastreamento da pilha e fornece informações completas enquanto que throw ex retorna as exceções até o ponto no qual o código throw ex foi excecutado, ai, ele remove os detalhes da exceção ate este ponto.

Entendeu agora por que dar preferência a lançar apenas throw ?

(disse Jesus) "Eu sou o pão vivo que desceu do céu; se alguém comer deste pão, viverá para sempre; e o pão que eu der é a minha carne, que eu darei pela vida do mundo. "
João 6:51

Referências:


José Carlos Macoratti