Compartilhar via


Usar exceções

No C#, os erros no programa em tempo de execução são propagados por meio do programa usando um mecanismo chamado exceções. As exceções são geradas pelo código que encontra um erro e capturadas pelo código que pode corrigir o erro. As exceções podem ser geradas pelo tempo de execução do .NET ou pelo código em um programa. Depois que uma exceção é gerada, ela se propaga pela pilha de chamadas até que uma instrução catch para a exceção seja encontrada. Exceções não capturadas são tratadas por um manipulador de exceção genérico fornecido pelo sistema que exibe uma caixa de diálogo.

As exceções são representadas por classes derivadas de Exception. Essa classe identifica o tipo de exceção e contém propriedades que têm detalhes sobre a exceção. Gerar uma exceção envolve a criação de uma instância de uma classe derivada de exceção, opcionalmente configurando propriedades da exceção e, em seguida, lançando o objeto usando a throw palavra-chave. Por exemplo:

class CustomException : Exception
{
    public CustomException(string message)
    {
    }
}
private static void TestThrow()
{
    throw new CustomException("Custom exception in TestThrow()");
}

Depois que uma exceção é lançada, o runtime verifica a instrução atual para ver se ela está dentro de um try bloco. Se estiver, todos os blocos catch associados ao bloco try serão verificados para ver se eles podem capturar a exceção. Catch blocos normalmente especificam tipos de exceção; se o tipo do catch bloco for do mesmo tipo que a exceção ou uma classe base da exceção, o catch bloco poderá manipular o método. Por exemplo:

try
{
    TestThrow();
}
catch (CustomException ex)
{
    System.Console.WriteLine(ex.ToString());
}

Se a instrução que lança uma exceção não estiver dentro de um bloco try ou se o bloco try que a encerra não tiver um bloco catch correspondente, o runtime verificará o método de chamada para uma instrução try e blocos catch. O runtime continuará acima na pilha de chamada, pesquisando um bloco catch compatível. Depois que o catch bloco é encontrado e executado, o controle é passado para a próxima instrução após esse catch bloco.

Uma try instrução pode conter mais de um catch bloco. A primeira catch instrução que pode lidar com a exceção é executada; todas as instruções a seguir catch , mesmo que sejam compatíveis, são ignoradas. Ordenar blocos catch do mais específico (ou mais derivado) para o menos específico. Por exemplo:

using System;
using System.IO;

namespace Exceptions
{
    public class CatchOrder
    {
        public static void Main()
        {
            try
            {
                using (var sw = new StreamWriter("./test.txt"))
                {
                    sw.WriteLine("Hello");
                }
            }
            // Put the more specific exceptions first.
            catch (DirectoryNotFoundException ex)
            {
                Console.WriteLine(ex);
            }
            catch (FileNotFoundException ex)
            {
                Console.WriteLine(ex);
            }
            // Put the least specific exception last.
            catch (IOException ex)
            {
                Console.WriteLine(ex);
            }
            Console.WriteLine("Done");
        }
    }
}

Antes que o catch bloco seja executado, o runtime verifica se há finally blocos. Os blocos Finally permitem que o programador limpe qualquer estado ambíguo que pode ser deixado de um bloco try cancelado ou libere quaisquer recursos externos (como identificadores de gráfico, conexões de banco de dados ou fluxos de arquivo) sem esperar o coletor de lixo no runtime finalizar os objetos. Por exemplo:

static void TestFinally()
{
    FileStream? file = null;
    //Change the path to something that works on your machine.
    FileInfo fileInfo = new System.IO.FileInfo("./file.txt");

    try
    {
        file = fileInfo.OpenWrite();
        file.WriteByte(0xF);
    }
    finally
    {
        // Closing the file allows you to reopen it immediately - otherwise IOException is thrown.
        file?.Close();
    }

    try
    {
        file = fileInfo.OpenWrite();
        Console.WriteLine("OpenWrite() succeeded");
    }
    catch (IOException)
    {
        Console.WriteLine("OpenWrite() failed");
    }
}

Se WriteByte() gerar uma exceção, o código no segundo bloco try que tentar reabrir o arquivo falharia se file.Close() não for chamado, e o arquivo permaneceria bloqueado. Como finally os blocos são executados mesmo se uma exceção for lançada, o finally bloco no exemplo anterior permite que o arquivo seja fechado corretamente e ajuda a evitar um erro.

Se nenhum bloco compatível catch for encontrado na pilha de chamadas após a geração de uma exceção, ocorrerá uma das três coisas:

  • Se a exceção estiver dentro de um finalizador, o finalizador será anulado e o finalizador base, se houver, será chamado.
  • Se a pilha de chamadas contiver um construtor estático ou um inicializador de campo estático, uma TypeInitializationException será gerada, com a exceção original atribuída à propriedade InnerException da nova exceção.
  • Se o início do thread for atingido, o thread será encerrado.