O tratamento de exceções é uma parte fundamental do desenvolvimento de software, garantindo que erros inesperados sejam tratados de maneira controlada e que a aplicação continue funcionando de forma estável. No C#, as exceções podem ser tratadas utilizando as estruturas “try”, “catch”, “finally” e “throw”. Neste artigo vamos explorar como utilizar essas estruturas para capturar, tratar e limpar recursos e como tratar exceções de forma global.
O que são exceções?
Exceções são condições anormais ou erros que ocorrem durante a execução de um programa. Elas podem ser causadas por uma variedade de razões, como falha em uma operação de leitura de arquivos, tentativa de divisão por zero, entre outros. O tratamento adequado dessas exceções é crucial para garantir que a aplicação não falhe silenciosamente e que o usuário receba uma resposta apropriada.
Bloco try
O bloco “try” é utilizado para encapsular código que pode gerar uma exceção. Se uma exceção ocorrer, a execução será desviada para o bloco “catch” correspondente (caso exista). Aqui está um exemplo de como usar o “try-catch” para tratar exceções:
try
{
var caminho = "D:\\Arquivo\\Inexistente\\arquivo.txt";
var conteudo = File.ReadAllText(caminho);
Console.WriteLine(conteudo);
}
catch (FileNotFoundException ex)
{
Console.WriteLine($"Erro: Arquivo não encontrado - {ex.Message}");
}
Neste exemplo, qualquer tentativa de leitura de um arquivo inexistente gerará uma exceção que será capturada pelo bloco catch.
Sem o uso de “try-catch”, o programa falharia abruptamente se ocorresse uma exceção. Por exemplo, tente executar o código abaixo sem tratamento de exceção:
var caminho = "D:\\Arquivo\\Inexistente\\arquivo.txt";
string conteudo = File.ReadAllText(caminho);
Console.WriteLine(conteudo);
Bloco catch
Como vimos acima, o “catch” captura e trata exceções que foram lançadas em um bloco “try”. Você pode definir múltiplos blocos “catch” para tratar tipos específicos de exceções.
try
{
int divisor = 0;
int resultado = 100 / divisor;
Console.WriteLine($"Resultado: {resultado}");
}
catch (DivideByZeroException)
{
Console.WriteLine("Erro: Não é possível dividir por zero.");
}
catch (Exception)
{
Console.WriteLine("Ocorreu um erro");
}
Neste exemplo, se “divisor” for zero, a exceção “DivideByZeroException” será capturada. Outros erros serão tratados pelo bloco genérico “catch”. Aqui a ordem de definição dos blocos catch é importante: exceções mais específicas devem ser tratadas primeiro, enquanto as mais genéricas, como a classe “Exception”, deve vir depois.
Ainda no bloco “catch” é importante notar que você pode ou não declarar uma variável para receber os detalhes da exceção. Note que no exemplo acima especificamos apenas o nome da classe de exceção que está sendo tratada. Dessa forma, capturamos a exceção, mas não temos acesso aos seus dados. Se desejarmos acessar maiores detalhes sobre o erro, podemos fazer da seguinte forma:
catch (Exception ex)
{
Console.WriteLine($"Erro genérico: {ex.Message}");
}
Agora a variável “ex” recebe a instância da exceção lançada e, a partir dela, podemos acessar os detalhes do erro. Aqui cabe destacar as propriedades Message e StackTrace das classes de exceção. A primeira traz uma mensagem descritiva sobre o problema, enquanto a segunda traz o detalhamento da pilha de chamadas no código que gerou a exceção. Essa informação é crucial para identificarmos qual trecho do código causou o erro.
Também é possível adicionar filtros no bloco “catch” para realizar tratamentos mais personalizados para cada cenário. Por exemplo:
public int GetItem(int index)
{
List items = [1, 2, 3, 4, 5];
try
{
return items[index];
}
catch(ArgumentOutOfRangeException) when (index < 0)
{
Console.WriteLine("Índice não pode ser negativo");
throw;
}
catch(ArgumentOutOfRangeException)
{
Console.WriteLine($"Índice incorreto. A lista só contém {items.Count} itens.");
throw;
}
}
No código acima estamos tratando o mesmo tipo de exceção duas vezes. Porém, no primeiro caso adicionamos um filtro para que o bloco “catch” só seja utilizado quando o parâmetro “index” for menor que zero. Para quaisquer outros valores inválidos de “index”, o segundo bloco “catch” será acionado.
Bloco finally
O bloco “finally” é utilizado para garantir que o código de limpeza será executado, independentemente de uma exceção ter sido lançada ou não. Ele pode ser usado com ou sem o bloco “catch” e é muito comum para realizar o descarte de recursos após algum processamento.
public void AbrirConexao()
{
SqlConnection conexao = null;
try
{
conexao = new SqlConnection("sua_conexao");
conexao.Open();
}
catch (SqlException ex)
{
Console.WriteLine($"Erro de SQL: {ex.Message}");
}
finally
{
if (conexao != null)
{
conexao.Close();
Console.WriteLine("Conexão fechada.");
}
}
}
Instrução throw
A instrução “throw” é utilizada para lançar uma exceção, seja criando uma nova ou re-lançando uma exceção já capturada. Isso permite que a exceção seja tratada em um nível superior ou forneça informações mais detalhadas sobre o erro.
Por exemplo, no código abaixo, se a idade for menor que 18, uma exceção “ArgumentException” é lançada:
public void ValidarIdade(int idade)
{
if (idade < 18)
{
throw new ArgumentException("A idade deve ser maior ou igual a 18 anos.");
}
}
Além disso, o “throw” pode ser usado dentro de um bloco “catch” para re-lançar uma exceção capturada, permitindo que ela seja tratada em outro nível, como mostrado abaixo:
try
{
if (idade < 18)
{
throw new ArgumentException("A idade deve ser maior ou igual a 18 anos.");
}
}
catch (Exception ex)
{
Console.WriteLine($"Log de erro: {ex.Message}");
throw;
}
Esse uso do “throw” ajuda a criar um fluxo de controle eficiente para exceções, possibilitando um tratamento mais flexível e organizado.
Acelere a sua carreira conosco!
Se você é Desenvolvedor .NET Júnior e quer acelerar sua carreira até nível Pleno com salário de R$7k+, ou mesmo busca a primeira vaga, conheça a Mentoria .NET Start: Clique aquiSe é Desenvolvedor .NET Pleno ou Sênior e quer virar referência técnica em sua equipe e mercado, com salário de R$10k+, conheça a Mentoria .NET Expert: Clique aqui
Conclusão
Adotar um tratamento de exceções eficiente não apenas melhora a qualidade do software, mas também oferece um nível superior de controle e previsibilidade, possibilitando que os desenvolvedores se antecipem a problemas e entreguem soluções que resistem às complexidades do mundo real.