Ir para o conteúdo do rodapé
Iron Academy Logo
Aprenda C#
Aprenda C#

Outras categorias

Tratamento de exceções em C#

Tim Corey
59m 46s

O tratamento de exceções é um aspecto crucial do desenvolvimento de aplicações robustas. O vídeo de Tim Corey sobre " Tratamento de Exceções em C# - Quando capturá-las, onde capturá-las e como capturá-las " fornece uma explicação detalhada do que são exceções, como tratá-las e onde tratá-las.

Este artigo tem como objetivo explicar o tratamento de exceções em C# usando o vídeo de Tim Corey. É um recurso poderoso que permite aos desenvolvedores gerenciar erros e condições excepcionais que surgem durante a execução do programa. Utilizando os blocos try, catch e finally, o C# fornece uma maneira estruturada de lidar com erros de tempo de execução, registrar exceções e manter o fluxo do programa.

Introdução

Tim começa explicando que muitos desenvolvedores têm uma visão incorreta das exceções e de como tratá-las. Ele enfatiza a importância de entender o que são exceções, onde e como lidar com elas adequadamente para criar aplicações mais robustas.

Criando um aplicativo de console de demonstração

Tim cria um aplicativo de console no Visual Studio 2017 para demonstrar o tratamento de exceções. Ele recomenda o uso de aplicativos de console para testar novos tópicos, pois exigem configuração mínima e são fáceis de usar.

using System;

namespace ExceptionsDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Placeholder for input and output operations
            Console.ReadLine();
        }
    }
}
using System;

namespace ExceptionsDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            // Placeholder for input and output operations
            Console.ReadLine();
        }
    }
}

Criando uma biblioteca de classe

Tim adiciona uma biblioteca de classes à solução para simular um cenário do mundo real onde diferentes métodos chamam uns aos outros.

Csharp Exception Handling 1 related to Criando uma biblioteca de classe

Ele exclui a classe padrão e cria uma nova classe chamada DemoCode.

public class DemoCode
{
    // Method to retrieve a number based on the provided position
    public int GetNumber(int position)
    {
        int[] numbers = { 1, 4, 7, 2 };
        return numbers[position];
    }

    // Intermediate method calls GetNumber
    public int ParentMethod(int position)
    {
        return GetNumber(position);
    }

    // Top-level method calls ParentMethod
    public int GrandparentMethod(int position)
    {
        return ParentMethod(position);
    }
}
public class DemoCode
{
    // Method to retrieve a number based on the provided position
    public int GetNumber(int position)
    {
        int[] numbers = { 1, 4, 7, 2 };
        return numbers[position];
    }

    // Intermediate method calls GetNumber
    public int ParentMethod(int position)
    {
        return GetNumber(position);
    }

    // Top-level method calls ParentMethod
    public int GrandparentMethod(int position)
    {
        return ParentMethod(position);
    }
}

A classe DemoCode contém métodos que se chamam mutuamente, recuperando, em última instância, um número de uma matriz com base na posição fornecida.

Simulação de uma exceção

Tim explica que o objetivo da aplicação é demonstrar fracassos, e não sucessos. Ele introduz uma exceção de índice fora dos limites ao passar uma posição inválida para o método GrandparentMethod.

DemoCode demo = new DemoCode();
int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
Console.WriteLine($"The value at the given position is {result}");
DemoCode demo = new DemoCode();
int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
Console.WriteLine($"The value at the given position is {result}");

Executar o código acima com uma posição inválida resulta em uma exceção IndexOutOfRangeException. Tim mostra como o depurador do Visual Studio destaca o problema e fornece informações detalhadas sobre a exceção.

Como NÃO usar try-catch

Tim explica um erro comum que os desenvolvedores cometem quando aprendem pela primeira vez sobre blocos try-catch. Eles costumam envolver todo o bloco de código onde esperam que uma exceção possa ocorrer, o que pode levar a um tratamento inadequado.

try
{
    int output = 0;
    output = numbers[position];
    return output;
}
catch (Exception ex)
{
    // Avoid returning default values that can mask the problem
    return 0;
}
try
{
    int output = 0;
    output = numbers[position];
    return output;
}
catch (Exception ex)
{
    // Avoid returning default values that can mask the problem
    return 0;
}

Tim destaca que essa abordagem é problemática porque oculta a exceção e continua a execução com suposições incorretas. Por exemplo, retornar 0 como valor padrão pode não ser apropriado e pode causar mais problemas.

Tratamento correto de exceções

Tim destaca que as exceções fornecem informações cruciais sobre estados inesperados na aplicação. Se a aplicação continuar nesse estado sem o devido tratamento, isso pode levar a mais erros e corrupção de dados.

Em vez de ignorar as exceções, é essencial tratá-las adequadamente. Eis uma abordagem melhor:

try
{
    return numbers[position];
}
catch (Exception ex)
{
    // Log the exception or handle it appropriately
    Console.WriteLine(ex.Message);
    throw; // Re-throw the exception to be handled by a higher-level handler
}
try
{
    return numbers[position];
}
catch (Exception ex)
{
    // Log the exception or handle it appropriately
    Console.WriteLine(ex.Message);
    throw; // Re-throw the exception to be handled by a higher-level handler
}

Ao relançar a exceção, você garante que o problema seja propagado e possa ser tratado em um nível superior, se necessário.

Fornecer informações úteis ao usuário

Tim explica que, embora algumas exceções possam ser tratadas de forma adequada sem causar a falha do aplicativo, é importante fornecer um feedback útil ao usuário. Por exemplo, exibir uma caixa de mensagem ou uma notificação com a opção de tentar novamente a operação.

Informações adicionais úteis: StackTrace

Tim demonstra como usar a propriedade StackTrace do objeto de exceção para obter informações detalhadas sobre onde a exceção ocorreu. Isso inclui a classe, o método e o número da linha, o que é essencial para a depuração.

try
{
    return numbers[position];
}
catch (Exception ex)
{
    Console.WriteLine(ex.StackTrace);
    throw;
}
try
{
    return numbers[position];
}
catch (Exception ex)
{
    Console.WriteLine(ex.StackTrace);
    throw;
}

A propriedade StackTrace fornece um rastreamento completo da pilha de chamadas, ajudando os desenvolvedores a identificar a localização exata do problema.

Posicionamento correto do try-catch

Tim explica que lidar corretamente com exceções não se resume apenas a capturá-las, mas também a saber onde posicionar os blocos try-catch. A chave é posicionar os blocos try-catch em um nível onde você tenha contexto suficiente para lidar com a exceção adequadamente.

Exemplo de posicionamento inadequado

Colocar um bloco try-catch em um nível profundo da pilha de chamadas geralmente não permite lidar com a exceção de forma eficaz, pois você não tem o contexto das operações de nível superior.

// Deep level exception handling (not ideal)
try
{
    return numbers[position];
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    throw;
}
// Deep level exception handling (not ideal)
try
{
    return numbers[position];
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    throw;
}

Exemplo de posicionamento adequado

Colocar o bloco try-catch no nível superior, como na interface do usuário ou no ponto de entrada do aplicativo, permite lidar com exceções considerando todo o contexto da operação.

try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}
try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

Dessa forma, você pode fornecer mensagens mais informativas ao usuário e decidir se o aplicativo pode continuar em execução ou se deve ser encerrado.

Informações de rastreamento de pilha

Tim enfatiza a importância das informações de rastreamento de pilha no diagnóstico de exceções. O rastreamento da pilha fornece um histórico detalhado das chamadas, mostrando onde a exceção ocorreu e a cadeia de chamadas de método que a originou.

try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}
try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

Essa saída fornece a localização exata da exceção e o caminho percorrido pelo código, facilitando a depuração e a correção do problema.

Demonstração de lógica de manipulação

Tim demonstra como lidar com a lógica no nível apropriado. Por exemplo, se um método for responsável por abrir e fechar uma conexão com o banco de dados, ele deverá tratar as exceções para garantir que os recursos sejam gerenciados adequadamente.

public int GrandparentMethod(int position)
{
    try
    {
        Console.WriteLine("Open database connection");
        int output = ParentMethod(position);
        Console.WriteLine("Close database connection");
        return output;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw; // Ensure the exception is propagated
    }
}
public int GrandparentMethod(int position)
{
    try
    {
        Console.WriteLine("Open database connection");
        int output = ParentMethod(position);
        Console.WriteLine("Close database connection");
        return output;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw; // Ensure the exception is propagated
    }
}

Neste exemplo, se ocorrer uma exceção, a conexão com o banco de dados não será fechada corretamente, o que pode levar a vazamentos de recursos. Ao adicionar um bloco try-catch, você pode garantir que a conexão seja fechada mesmo se ocorrer uma exceção.

Usando o bloco finally

Tim apresenta o bloco finally, que garante que determinado código seja executado independentemente da ocorrência de uma exceção. Isso é particularmente útil para liberar recursos, como fechar conexões com o banco de dados.

public int GrandparentMethod(int position)
{
    try
    {
        Console.WriteLine("Open database connection");
        int output = ParentMethod(position);
        return output;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw; // Re-throw the exception to ensure it's handled by a higher-level handler
    }
    finally
    {
        Console.WriteLine("Close database connection");
    }
}
public int GrandparentMethod(int position)
{
    try
    {
        Console.WriteLine("Open database connection");
        int output = ParentMethod(position);
        return output;
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.Message);
        throw; // Re-throw the exception to ensure it's handled by a higher-level handler
    }
    finally
    {
        Console.WriteLine("Close database connection");
    }
}

O bloco finally é executado após os blocos try e catch, garantindo que a conexão seja fechada mesmo se uma exceção for lançada.

A declaração de arremesso

Tim explica a importância de relançar exceções para passá-las adiante na pilha de chamadas. Isso permite que os manipuladores de nível superior processem as exceções adequadamente.

catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    throw; // Re-throws the exception to be handled by the calling method
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    throw; // Re-throws the exception to be handled by the calling method
}

Relançando a exceção com throw; Garante que todo o rastreamento da pilha seja preservado, fornecendo um contexto valioso para a depuração.

Aumentando as exceções corretamente

Tim demonstra como as exceções se propagam pela pilha de chamadas. Cada método verifica a existência de um bloco try-catch e, em seguida, trata a exceção ou a repassa para o chamador.

try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}
try
{
    int result = demo.GrandparentMethod(4); // This will cause an IndexOutOfRangeException
    Console.WriteLine($"The value at the given position is {result}");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

Neste exemplo, o método GrandparentMethod captura a exceção, registra-a e a relança. O bloco try-catch de nível superior no aplicativo de console trata a exceção e exibe a mensagem de erro e o rastreamento da pilha.

Erros comuns no tratamento de exceções

Tim destaca vários erros comuns que os desenvolvedores cometem ao lidar com exceções:

  1. Usando throw ex;:

    • Reescrever o rastreamento da pilha e perder contexto valioso.

    • Exemplo:
      catch (Exception ex)
      {
      // Incorrect
      throw ex; // Rewrites stack trace
      }
      catch (Exception ex)
      {
      // Incorrect
      throw ex; // Rewrites stack trace
      }
  2. Lançando uma nova exceção :

    • Criar uma nova exceção com uma mensagem personalizada, mas perder o rastreamento de pilha original.

    • Exemplo:
      catch (Exception ex)
      {
      // Incorrect
      throw new Exception("I blew up");
      }
      catch (Exception ex)
      {
      // Incorrect
      throw new Exception("I blew up");
      }

Criando uma nova exceção sem perder o rastreamento de pilha original.

Tim explica como criar uma nova exceção preservando o rastreamento de pilha original. Isso pode ser útil quando você deseja fornecer uma mensagem de erro mais significativa ou um tipo de exceção diferente, mantendo ainda o contexto do erro original.

catch (Exception ex)
{
    throw new ArgumentException("You passed in bad data", ex);
}
catch (Exception ex)
{
    throw new ArgumentException("You passed in bad data", ex);
}

Ao passar a exceção original (ex) como exceção interna, você mantém o rastreamento de pilha original, o que é crucial para a depuração.

Preservando informações de rastreamento de pilha

Tim demonstra como acessar a mensagem e o rastreamento de pilha da exceção original ao criar uma nova exceção.

catch (Exception ex)
{
    Console.WriteLine("You passed in bad data");
    Console.WriteLine(ex.StackTrace);
    throw new ArgumentException("You passed in bad data", ex);
}
catch (Exception ex)
{
    Console.WriteLine("You passed in bad data");
    Console.WriteLine(ex.StackTrace);
    throw new ArgumentException("You passed in bad data", ex);
}

Isso garante que a exceção lançada na pilha contenha tanto a nova mensagem quanto os detalhes da exceção original.

Percorrendo exceções internas

Tim fornece um método para percorrer todas as exceções internas a fim de extrair suas mensagens e rastreamentos de pilha.

catch (Exception ex)
{
    Exception inner = ex;
    while (inner != null)
    {
        Console.WriteLine(inner.StackTrace);
        inner = inner.InnerException;
    }
    throw;
}
catch (Exception ex)
{
    Exception inner = ex;
    while (inner != null)
    {
        Console.WriteLine(inner.StackTrace);
        inner = inner.InnerException;
    }
    throw;
}

Este loop itera por cada exceção interna, imprimindo seu rastreamento de pilha, garantindo que todas as camadas de exceções sejam consideradas.

Tratamento diferenciado de diferentes exceções

Tim discute como lidar com diferentes tipos de exceções usando múltiplos blocos catch. Isso permite um tratamento específico com base no tipo de exceção.

try
{
    // Code that might throw an exception
}
catch (ArgumentException ex)
{
    Console.WriteLine("You gave us bad information. Bad user!");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}
try
{
    // Code that might throw an exception
}
catch (ArgumentException ex)
{
    Console.WriteLine("You gave us bad information. Bad user!");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

Neste exemplo, a exceção ArgumentException é tratada especificamente imprimindo uma mensagem personalizada, enquanto todas as outras exceções recorrem a um manipulador geral que imprime a mensagem de exceção e o rastreamento da pilha.

Csharp Exception Handling 2 related to Tratamento diferenciado de diferentes exceções

Importância da ordem em múltiplos blocos de captura

Tim enfatiza a importância da ordem ao usar vários blocos de captura. As exceções mais específicas devem ser tratadas primeiro, seguidas pelas exceções mais gerais.

try
{
    // Code that might throw an exception
}
catch (ArgumentException ex)
{
    Console.WriteLine("You gave us bad information. Bad user!");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}
try
{
    // Code that might throw an exception
}
catch (ArgumentException ex)
{
    Console.WriteLine("You gave us bad information. Bad user!");
}
catch (Exception ex)
{
    Console.WriteLine(ex.Message);
    Console.WriteLine(ex.StackTrace);
}

Se um bloco catch mais genérico aparecer antes de um bloco catch específico, ele capturará todas as exceções, e o bloco catch específico nunca será alcançado, o que levará a erros de compilação.

Conclusão

O guia em vídeo avançado de Tim Corey sobre tratamento de exceções em C# aborda técnicas essenciais para criar novas exceções, preservar rastreamentos de pilha e usar vários blocos catch de forma eficaz. Seguindo as melhores práticas, os desenvolvedores podem criar aplicativos robustos que lidam com exceções de forma adequada e fornecem informações valiosas para depuração.

Hero Worlddot related to Tratamento de exceções em C#
Hero Affiliate related to Tratamento de exceções em C#

Ganhe mais compartilhando o que você ama.

Você cria conteúdo para desenvolvedores que trabalham com .NET, C#, Java, Python ou Node.js? Transforme sua expertise em renda extra!

Equipe de suporte de ferro

Estamos online 24 horas por dia, 5 dias por semana.
Bater papo
E-mail
Liga para mim