This article has been localized into Portuguese by the community.
Manipulação de exceção
Em todo programa, as coisas dão errado às vezes. Com o C#, somos abençoados com um bom compilador, o que nos ajudará a evitar alguns dos erros mais comuns. Obviamente, ele não pode ver todos os erros que podem acontecer e, nesses casos, o .NET framework lança uma exceção, para nos dizer que algo deu errado. Em um capítulo anterior, sobre arrays, descrevi como obteríamos uma exceção se tentássemos colocar muitos itens em uma matriz. Vamos trazer o exemplo:
using System;
using System.Collections;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
int[] numbers = new int[2];
numbers[0] = 23;
numbers[1] = 32;
numbers[2] = 42;
foreach(int i in numbers)
Console.WriteLine(i);
Console.ReadLine();
}
}
}
Ok, tente executar este exemplo e você verá o que estou falando. Você vê o que estamos fazendo errado? Definimos uma matriz de inteiros com espaço para 2 itens, mas tentamos usar 3 espaços. Obviamente, isso leva a um erro, que você verá se tentar executar este exemplo. Quando executado dentro do Visual Studio, o IDE nos dá algumas opções para a exceção, mas se você tentar executar o programa simplesmente clicando duas vezes no arquivo EXE, você receberá um erro desagradável. Se você sabe que um erro pode ocorrer, você deve lidar com isso. Aqui é onde as exceções são usadas. Aqui está uma versão ligeiramente modificada do código acima:
int[] numbers = new int[2];
try
{
numbers[0] = 23;
numbers[1] = 32;
numbers[2] = 42;
foreach(int i in numbers)
Console.WriteLine(i);
}
catch
{
Console.WriteLine("Something went wrong!");
}
Console.ReadLine();
Deixe-me apresentar-lhe seu novo melhor amigo quando se trata de tratamento de erros: o bloco try..catch. Tente executar o programa agora e veja a diferença - em vez de o Visual Studio/Windows nos informar que um problema sério ocorreu, podemos contar nossa própria história. Mas não seria bom se pudéssemos dizer o que deu errado? Sem problemas:
catch(Exception ex)
{
Console.WriteLine("An error occured: " + ex.Message);
}
Como você pode ver, adicionamos algo à instrução catch. Agora nós dizemos qual exceção queremos capturar, neste caso a base de todas as exceções, a Exceção. Ao fazer isso, obtemos algumas informações sobre o problema que causou a exceção e, ao emitir a propriedade Message, obtemos uma descrição compreensível do problema.
Como eu disse, Exception é o tipo mais geral de exceção. As regras de tratamento de exceções nos dizem que devemos sempre usar o tipo de exceção menos geral e, nesse caso, sabemos exatamente o tipo exato de exceção gerado pelo nosso código. Como? Porque o Visual Studio nos disse quando não lidamos com isso. Em caso de dúvida, a documentação geralmente descreve as exceções que um método pode lançar. Outra maneira de descobrir é usar a classe Exception para nos informar - altere a linha de saída para isso:
Console.WriteLine("An error occured: " + ex.GetType().ToString());
O resultado é, conforme esperado, IndexOutOfRangeException. Portanto, devemos manipular essa exceção, mas nada nos impede de lidar com mais de uma exceção. Em algumas situações, você pode querer fazer coisas diferentes, dependendo de qual exceção foi lançada. Simplesmente mude o nosso bloco catch para o seguinte:
catch(IndexOutOfRangeException ex)
{
Console.WriteLine("An index was out of range!");
}
catch(Exception ex)
{
Console.WriteLine("Some sort of error occured: " + ex.Message);
}
Como você pode ver, procuramos primeiro o IndexOutOfRangeException. Se fizéssemos o contrário, o bloco catch com a classe Exception iria obtê-lo, porque todas as exceções derivam dele. Então, em outras palavras, você deve usar as exceções mais específicas primeiro.
Mais uma coisa que você deve saber sobre exceções é o bloco finally. O bloco finally pode ser adicionado a um conjunto de blocos catch, ou ser usado exclusivamente, dependendo de suas necessidades. O código dentro do bloco finally é sempre executado - exceção ou exceção. É um bom lugar se você precisar fechar referências de arquivos ou descartar objetos desnecessários. Como nossos exemplos foram bem simples até agora, não precisávamos de nenhuma limpeza, já que o coletor de lixo lida com isso. Mas, como provavelmente irá se deparar com situações em que você precisa do bloco finally, aqui está uma versão estendida do nosso exemplo:
int[] numbers = new int[2];
try
{
numbers[0] = 23;
numbers[1] = 32;
numbers[2] = 42;
foreach(int i in numbers)
Console.WriteLine(i);
}
catch(IndexOutOfRangeException ex)
{
Console.WriteLine("An index was out of range!");
}
catch(Exception ex)
{
Console.WriteLine("Some sort of error occured: " + ex.Message);
}
finally
{
Console.WriteLine("It's the end of our try block. Time to clean up!");
}
Console.ReadLine();
Se você executar o código, verá que o primeiro bloco de exceção e o bloco finally são executados. Se você remover a linha que adiciona o número 42 ao array, você verá que somente o bloco finally é atingido.
Outra parte importante que você deve saber sobre exceções é como elas afetam o método no qual as exceções ocorrem. Nem todas as exceções não tratadas são fatais para seu aplicativo, mas quando não são, você não deve esperar que o código restante do método seja executado. Por outro lado, se você lidar com a exceção, somente as linhas após o bloco try serão executadas. Em nosso exemplo, o loop que gera os valores da matriz nunca é alcançado, porque o bloco try vai direto para o (s) bloco (s) catch/finally (s) assim que uma exceção é lançada. No entanto, a última linha, onde lemos no console para impedir que o aplicativo seja encerrado imediatamente, é atingida. Você deve sempre ter isso em mente ao construir blocos try.