Manipulação de exceções (Guia de Programação em C#)

Um bloco try é usado por programadores de C# para particionar o código que pode ser afetado por uma exceção. Os blocos catch associados são usados para tratar qualquer exceção resultante. Um bloco finally contém código que será executado de uma exceção ser ou não ser lançada no bloco try, como a liberação de recursos que estão alocados no bloco try. Um bloco try exige um ou mais blocos catch associados ou um bloco finally ou ambos.

Os exemplos a seguir mostram uma instrução try-catch, uma instrução try-finally e um instrução try-catch-finally.

try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
    // Only catch exceptions that you know how to handle.
    // Never catch base class System.Exception without
    // rethrowing it at the end of the catch block.
}
try
{
    // Code to try goes here.
}
finally
{
    // Code to execute after the try block goes here.
}
try
{
    // Code to try goes here.
}
catch (SomeSpecificException ex)
{
    // Code to handle the exception goes here.
}
finally
{
    // Code to execute after the try (and possibly catch) blocks
    // goes here.
}

Um bloco try sem um bloco catch ou finally causa um erro do compilador.

Blocos catch

Um bloco catch pode especificar o tipo de exceção a ser capturado. A especificação de tipo é chamada de filtro de exceção. O tipo de exceção deve ser derivado de Exception. Em geral, não especifique Exception como o filtro de exceção, a menos que você saiba como tratar todas as exceções que podem ser lançadas no bloco try ou tenha incluído uma instruçãothrow no final do seu bloco catch.

Vários blocos catch com filtros de exceção diferentes podem ser encadeados. Os blocos catch são avaliados de cima para baixo no seu código, mas somente um bloco catch será executado para cada exceção que é lançada. O primeiro bloco catch que especifica o tipo exato ou uma classe base da exceção lançada será executado. Se nenhum bloco catch especificar uma classe de exceção correspondente, um bloco catch que não tem nenhum tipo será selecionado, caso haja algum na instrução. É importante posicionar os blocos catch com as classes de exceção mais específicas (ou seja, os mais derivados) primeiro.

Capture exceções quando as seguintes condições forem verdadeiras:

  • Você tem uma boa compreensão de porque a exceção seria lançada e pode implementar uma recuperação específica, como solicitar que o usuário insira um novo nome de arquivo, quando você capturar um objeto FileNotFoundException.
  • Você pode criar e lançar uma exceção nova e mais específica.
    int GetInt(int[] array, int index)
    {
        try
        {
            return array[index];
        }
        catch (IndexOutOfRangeException e)
        {
            throw new ArgumentOutOfRangeException(
                "Parameter index is out of range.", e);
        }
    }
    
  • Você deseja tratar parcialmente uma exceção antes de passá-la para mais tratamento. No exemplo a seguir, um bloco catch é usado para adicionar uma entrada a um log de erros antes de lançar novamente a exceção.
    try
    {
        // Try to access a resource.
    }
    catch (UnauthorizedAccessException e)
    {
        // Call a custom error logging procedure.
        LogError(e);
        // Re-throw the error.
        throw;
    }
    

Você também pode especificar filtros de exceção para adicionar uma expressão booliana a uma cláusula catch. Filtros de exceção indicam que uma cláusula catch específica corresponde somente quando essa condição é verdadeira. No exemplo a seguir, ambas as cláusulas catch usam a mesma classe de exceção, mas uma condição extra é verificada para criar uma mensagem de erro diferente:

int GetInt(int[] array, int index)
{
    try
    {
        return array[index];
    }
    catch (IndexOutOfRangeException e) when (index < 0) 
    {
        throw new ArgumentOutOfRangeException(
            "Parameter index cannot be negative.", e);
    }
    catch (IndexOutOfRangeException e)
    {
        throw new ArgumentOutOfRangeException(
            "Parameter index cannot be greater than the array size.", e);
    }
}

Um filtro de exceção que sempre retorna false pode ser usado para examinar todas as exceções, mas não processá-las. Um uso típico é registrar exceções:

public class ExceptionFilter
{
    public static void Main()
    {
        try
        {
            string? s = null;
            Console.WriteLine(s.Length);
        }
        catch (Exception e) when (LogException(e))
        {
        }
        Console.WriteLine("Exception must have been handled");
    }

    private static bool LogException(Exception e)
    {
        Console.WriteLine($"\tIn the log routine. Caught {e.GetType()}");
        Console.WriteLine($"\tMessage: {e.Message}");
        return false;
    }
}

O método LogException sempre retorna false, nenhuma cláusula catch que usa esse filtro de exceção corresponde. A cláusula catch pode ser geral, usando System.Exception, e cláusulas posteriores podem processar classes de exceção mais específicas.

Blocos Finally

Um bloco finally permite que você limpe as ações que são realizadas em um bloco try. Se estiver presente, o bloco finally será executado por último, depois do bloco try e de qualquer bloco catch de correspondência. Um bloco finally sempre é executado, independentemente de uma exceção ser lançada ou de um bloco catch correspondente ao tipo de exceção ser encontrado.

O bloco finally pode ser usado para liberar recursos, como fluxos de arquivos, conexões de banco de dados e identificadores de gráficos, sem esperar que o coletor de lixo no runtime finalize os objetos.

No exemplo a seguir, o bloco finally é usado para fechar um arquivo está aberto no bloco try. Observe que o estado do identificador de arquivo é selecionado antes do arquivo ser fechado. Se o bloco try não puder abrir o arquivo, o identificador de arquivo ainda terá o valor null e o bloco finally não tentará fechá-lo. Em vez disso, se o arquivo for aberto com êxito no bloco try, o bloco finally fechará o arquivo aberto.

FileStream? file = null;
FileInfo fileinfo = new System.IO.FileInfo("./file.txt");
try
{
    file = fileinfo.OpenWrite();
    file.WriteByte(0xF);
}
finally
{
    // Check for null because OpenWrite might have failed.
    file?.Close();
}

Especificação da Linguagem C#

Para obter mais informações, veja Exceções e A declaração try na Especificação da Linguagem C#. A especificação da linguagem é a fonte definitiva para a sintaxe e o uso de C#.

Confira também