Sie sind auf Seite 1von 8

Tratamento de exceções com C#

Não importa o quão correto seja o seu código, seus programas sempre
devem estar preparados para tratar erros. Por exemplo, durante um
processamento complexo seu código pode descobrir que não term
permissão para ler um arquivo, ou enquanto ele está enviando uma
requisição por rede, a conexão de rede pode cair.

Nessas situações excepcionais, não é suficiente que um método retorna


um código de erro – pode haver vários outros pontos do programa
dependentes deste onde o erro ocorreu, então o que você realmente
quer é que o programa relate o erro para o usuário de forma tranqüila e
se recupere do erro para que não trave ou não feche abruptamente. A
linguagem c# possui ótimas formas de tratar esse tipo de situação
através de um mecanismo chamado tratamento de exceções (exception
handling).

Classes de exceções

Em c#, uma exceção é um objeto criado (ou lançado) quando uma


condição particular excepcional de erro ocorre. Esse objeto contém
informações que podem ajudar a capturar e tratar o problema. Embora
possamos criar nossas próprias classes de exceções, o DotNet nos dá
muitas classes de exceções predefinidas.

Capturando exceções

Já que o DotNet inclui um leque de classes de exceções, como você


pode usá-las para capturar condições de erro em seu código? Para lidar
com possíveis condições de erro em seu código c#, você normalmente
vai dividir a parte relevante do seu programa em blocos de três tipos
diferentes:

• Blocos try que contém código que forma a parte normal das
operações do programa, mas que podem encontrar algumas
condições sérias de erro.

• Blocos catch que contém código que lida com as várias condições
de erro que podem aparecer enquanto o código do bloco try está
sendo processado.
• Blocos finally que contém o código que desaloca recursos ou
executa qualquer outra ação ao final de um bloco try ou de um
bloco catch. É importante entender que o bloco finally será
executado independentemente de uma exceção ter sido lançada.
Uma vez que colocamos código de tarefas de finalização que
julgarmos que sempre deva ser executado no bloco finally, o
bloco finally é opcional.

Então como fazer esses blocos trabalharem juntos para capturar as


condições de erro? Vamos lá:

1. O fluxo de execução entra no bloco try


2. Se nenhum erro ocorrer, a execução prossegue normalmente pelo
bloco try, e quando chega ao fim desse bloco, o fluxo pula para o
bloco finally (passo 5). Contudo, se um erro ocorre dentro do
bloco try, um objeto de erro é lançado e a execução pula para o
bloco catch imediatamente (próximo passo).
3. A condição de erro é tratada no bloco catch.
4. Ao final do bloco catch a execução vai naturalmente para o bloco
finally.
5. O bloco finally é executado.

A sintaxe usada no c# para utilizar os blocos try, catch e finally é a


seguinte:

try
{
//código de execução normal
}
catch
{
//tratamento do erro
}
finally
{
//finalização
}

Algumas variações da estrutura acima podem ser:


• Omissão do bloco finally
• Obtenção do objeto de exceção no código catch
• Utilização de vários blocos catch
• Omissão do bloco catch para que haja apenas os blocos try e
finally.
Tudo muito bom, tudo muito bem, mas a pergunta que não quer calar
é: se o código está rodando no bloco try, como ele sabe quando saltar
para o bloco catch se um erro ocorre?

Se um erro é detectado, o código faz uma coisa que é conhecida como


lançamento de uma exceção. Em outras palavras, ele cria um objeto
baseado em uma classe de exceção e o lança, mais ou menos assim:

throw new OverflowException();

Aqui nós instanciamos um objeto de exceção da classe


OverflowException. Assim que o computador encontra uma instrução de
lançamento de exceção (throw) dentro de um bloco try, ele
imediatamente procura por um bloco catch associado com o bloco try.
Se houver mais de um bloco catch associado com o bloco try, ele
identifica o bloco catch correto checando que classe de exceção está
associada a que bloco catch.

Por exemplo, quando o objeto do tipo OverflowException é lançado, a


execução salta para o seguinte bloco catch:

catch (OverflowException e)
{
...
}

Em outras palavras, o computador procura pelo bloco catch que indica


um objeto de exceção cuja classe se encaixa com a classe do objeto de
exceção lançado. Pode ser uma a mesma classe ou uma classe base.

Implementando múltiplos blocos catch

A forma mais simples de ver os blocos try...catch...finally funcionando


na prática é com exemplos. No exemplo a seguir pediremos um número
ao usuário repetidamente e depois o mostraremos. Contudo, para deixar
o exemplo interessante, vamos imaginar que o número tem que estar
entre 0 e 5. Caso contrário, o programa não processará o número
adequadamente. Sendo assim lançaremos uma exceção se o usuário
digitar qualquer coisa que esteja fora desse intervalo.

using System;
namespace TryCatchException
{
public class Teste
{
public static void Main()
{
string userInput;
bool digitou = true;
while (digitou)
{
try
{
Console.Write(“Insira um número entre 0 e 5
“ +
“(ou pressione Enter para sair“);
userInput = Console.ReadLine();
if (userInput == “”)
{
digitou = false; //quer sair
}
/* aqui pode ser lançada uma exceção de
conversão se o usuário digitar texto ao
invés de número
*/
int index = Convert.ToInt32(userInput);
if (index < 0 || index > 5)
{
//lançamos uma exceção
throw new IndexOutOfRangeException(
“You typed in “ + userInput);
}
//imprime o número
Console.WriteLine(“Você digitou: “ + index);
} //fim do try
catch (IndexOutOfRangeException e)
{
Console.WriteLine(“Exception: “ +
“Número deve estar entre 0 e 5. “ +
e.Message);
} //fim do catch
catch (Exception e)
{
Console.WriteLine(
“Ocorreu uma exceção conhecida : “ +
e.Message);
} //fim do catch
catch
{
Console.WriteLine(“Alguma exceção
desconhecida ocorreu.”);
} //fim do catch
finally
{
Console.WriteLine(“Obrigado.”);
}//fim do finally
}//fim do while
}//fim do método Main
}//fim da classe Teste
}//fim do namespace TryCatchException
A parte principal desse código é laço de repetição while, que
continuamente utiliza o método Console.ReadLine() para pedir ao
usuário uma nova entrada de valor. Checamos para ver se o usuário
simplesmente apertou Enter porque neste caso, imaginamos que ele
deseja sair do programa.

O método Convert.ReadLine() retorna uma string, então nossa primeira


tarefa é convertê-la para um int usando o método Convert.ToInt32().
Neste ponto o compilador pode lançar uma exceção caso o usuário
tenha digitado um valor que não possa ser convertido corretamente
para inteiro.

Observe que colocamos no bloco finally uma mensagem de


agradecimento ao usuário. Isso significa que não importanto se ocorra
uma exceção ou não, no final o usuário receberá uma mensagem de
agradecimento.

Embora o compilador esteja atento a exceções que possam ocorrer, o


código acima mostra que nós também podemos criar condições de
exceção. Nós checamos se o valor convertido para inteiro está dentro do
intervalo entre 0 e 5. Caso não esteja lançamos uma exceção do tipo
IndexOutOfRangeException e passamos como parâmetro a mensagem
de erro da exceção.

O DotNet contém muitas classes de exceção que são derivadas da classe


System.Exception. Cada uma delas cuida de um tipo particular de
condição de exceção.

Ao ser lançada uma exceção (seja pelo nosso código ou pelo


compilador), o fluxo de processamento saltará direto para o bloco catch
correspondente a exceção que ocorreu.

Repare que utilizamos três blocos catch. Um para tratar especificamente


o problema de números que não estão no intervalo entre 0 e 5
(IndexOutOfRangeException). Também criamos um para erros genéricos
(Exception) para onde a execução irá, se por exemplo ocorrer um erro
de conversão.

Quando utilizamos vários blocos catch, em geral colocamos primeiro os


tratamentos de exceções mais específicas e depois os tratamentos de
exceções mais genéricas.

Observe que criamos ainda um terceiro bloco catch que não especifica o
tipo de exceção. Esse bloco é para o caso em que a exceção ocorrida
não consegue ser tratada por nenhuma classe de exceção do DotNet.
Isso é útil por exemplo quando um erro de sistema inesperado ocorre,
ou quando utilizamos em nossos programas bibliotecas de linguagens
antigas que embora funcionem no DotNet não são completamente
gerenciáveis por ele (como bibliotecas de C++ por exemplo).

Algumas propriedades de um objeto de exceção

No exemplo anterior, utilizamos a propriedade Message do objeto de


exceção. Contudo, existem algumas outras propriedades interessantes
disponíveis:

HelpLink: É um link para um arquivo de help que dá mais informações


sobre a exceção.

Message: É um texto que descreve as condições do erro.

Source: É o nome do aplicativo ou objeto que causou a exceção.

StackTrace: Dá detalhes sobre a chamada de métodos na pilha (para


ajudar a descobrir em qual método a exceção foi lançada)

TargetSite: É um objeto refletido do DotNet que descreve o método que


lançou a exceção.

InnerException: Se a exceção foi lançada de dentro de um bloco catch,


essa propriedade contém o objeto de exceção que enviou o código para
dentro do bloco catch.

Dessas propriedades, StackTrace, TargetSite e InnerException são


dadas automaticamente pelo ambiente de execução do DotNet enquanto
que as demais (Source, HelpLink e Message) podem ser configuradas
caso você mesmo crie uma classe de exceção. Por exemplo:

if (ErrorCondition == true)
{
Exception myException = new
ClassmyException(“Help!!!!”);
myException.Source = “My Application Name”;
myException.HelpLink = “MyHelpFile.txt”;
throw myException;
}
Blocos de tratamento aninhados

Uma característica muito interessante das exceções é que você pode


aninhar blocos try colocando um dentro do outro. Assim, por exemplo:

try
{
// Ponto A
try
{
// Ponto B
}
catch
{
// Ponto C
}
finally
{
// finalização
}
// Ponto D
}
catch
{
// tratamento de erro
}
finally
{
// finalização
}

Vamos entender como isso funciona. Se uma exceção for lançada dentro
do bloco try mais externo mas fora do bloco try mais interno (pontos A e
D), então a situação de exceção é capturada pelo bloco catch mais
externo e o bloco finally mais externo é executado. Se uma exceção é
lançada dentro do bloco try interno (ponto B), e a exceção será tratada
pelo bloco catch interno e o bloco finally interno será executado antes
da execução continuar o bloco try externo (no ponto D).

E o mais interessante: se ocorrer uma exceção no bloco catch interno


(ponto C), essa exceção será entendida como se tivesse sido lançada
pelo bloco try mais externo, então o fluxo de execução imediatamente
abandonará o bloco catch interno e executará o bloco finally mais
interno, para logo em seguida saltar para o bloco catch externo para o
tratamento dessa exceção. Isso também acontece, se a exceção ocorrer
no bloco finally interno, o fluxo de execução é imediatamente desviado
para o bloco catch externo.

Embora nós tenhamos visto uma situação com apenas dois blocos try
aninhados, o mesmo princípio continua valendo não importa quantos
blocos try você aninhar dentro de outros. A cada estágio, o ambiente de
execução do DotNet irá transferir suavemente o controle para o bloco
catch mais conveniente.

Blocos aninhados de tratamento de exceção são importantes para:

• Modificação do tipo de exceção lançada


• Habilidade de tratamento de diferentes tipos de exceção em
diferentes pontos do seu código

O que acontece se uma exceção não for tratada?

Algumas vezes uma exceção pode ser lançada, mas não há bloco catch
em seu código que esteja apto a tratá-la. O que acontece então?

O ambiente de execução do DotNet irá capturar a exceção. Contudo, os


resultados, nesse caso, não são muito interessantes. A execução do seu
código será terminada abruptamente e será mostrada ao usuário uma
caixa de diálogo com uma reclamação de que o código não tratou
determinada exceção, assim com detalhes sobre a exceção dados pelo
DotNet, os quais o usuário provavelmente não entenderá.

Por isso, em geral, se você está criando um programa, você deve tentar
capturar o máximo de exceções que você puder, e tratá-las de forma
conveniente. De uma forma geral, as situações que costumam gerar
exceções são situações onde se depende de um fator externo para que o
programa funcione como:

• Interação com o usuário


• Acesso a arquivos
• Acesso a banco de dados
• Interação com outros computadores através de uma rede de
computadores

Das könnte Ihnen auch gefallen