Compartilhar via


Práticas recomendadas para tratamento de exceções

Um conjunto bem-estruturado de blocos de código de manipulação de erros pode tornar um programa mais robusto e menos propenso a falhas porque o aplicativo lida com esses erros. A lista a seguir contém sugestões sobre as práticas recomendadas para tratamento de exceções:

  • Saiba quando configurar um bloco try/catch. Por exemplo, você pode verificar programaticamente uma condição que é provável de ocorrer sem usar tratamento de exceções. Em outras situações, usar tratamento de exceções para capturar uma condição de erro é apropriado.

    O exemplo a seguir usa uma instrução if para verificar se uma conexão é fechada. Você pode usar esse método em vez de lançar uma exceção se a conexão não for fechada.

If conn.State <> ConnectionState.Closed Then
    conn.Close()
End IF
if (conn.State != ConnectionState.Closed)
{
    conn.Close();
}
if (conn->State != ConnectionState::Closed)
{
    conn->Close();
}

No exemplo a seguir, uma exceção é lançada se a conexão não for fechada.

Try
    conn.Close()
Catch ex As InvalidOperationException
    Console.WriteLine(ex.GetType().FullName)
    Console.WriteLine(ex.Message)
End Try
try
{
    conn.Close();
}
catch (InvalidOperationException ex)
{
    Console.WriteLine(ex.GetType().FullName);
    Console.WriteLine(ex.Message);
}
try
{
    conn->Close();
}
catch (InvalidOperationException^ ex)
{
    Console::WriteLine(ex->GetType()->FullName);
    Console::WriteLine(ex->Message);
}

O método escolhido depende da frequência com que você espera que o evento ocorra. Se o evento for realmente excepcional e for um erro (como um final de arquivo inesperado), usar tratamento de exceções é melhor porque menos código é executado no caso normal. Se o evento ocorre com frequência, usar o método programático para verificar erros é melhor. Nesse caso, se ocorrer uma exceção, a exceção levará mais tempo para ser tratada.

  • Use blocos try/finally ao redor de códigos que potencialmente podem gerar uma exceção e centralize suas instruções catch em um local. Dessa forma, a instrução try gera a exceção, a instrução finally fecha ou desaloca recursos, e a instrução catch lida com a exceção a partir de um local central.

  • Sempre ordene exceções em blocos catch da mais específica para a menos específica. Essa técnica lida com a exceção específica antes que ela seja passada para um bloco catch mais geral.

  • Termine nomes de classes de exceção com a palavra "Exception". Por exemplo:

Public Class MyFileNotFoundException
    Inherits Exception
End Class
public class MyFileNotFoundException : Exception
{
}
public ref class MyFileNotFoundException : public Exception
{
};
  • Ao criar exceções definidas pelo usuário, você deve garantir que os metadados para as exceções estejam disponíveis para códigos executando remotamente, inclusive quando exceções ocorrem entre domínios de aplicativo. Por exemplo, suponha que domínio de aplicativo A cria o domínio de aplicativo B, que executa o código que lança uma exceção. Para o domínio de aplicativo a detectar e tratar a exceção corretamente, deve ser capaz de encontrar o assembly que contém a exceção lançada pelo domínio de aplicativo b. Se o domínio de aplicativo b lança uma exceção que está contida em um assembly em sua base de aplicativo, mas não em será o domínio de aplicativo do aplicativo de base, o domínio de aplicativo a não ser capaz de encontrar a exceção e o common language runtime lança um FileNotFoundException. Para evitar essa situação, você pode implantar o assembly que contém as informações de exceção de duas maneiras:

    • Coloque o assembly em uma base de aplicativo comum compartilhada por ambos os domínios de aplicativo

      - ou -

    • Se os domínios não compartilham uma base de aplicativos comum, assine o assembly que contém as informações de exceção com um nome forte e implante o assembly no cache global de assemblies.

  • Em C# e C++, use pelo menos os três construtores comuns ao criar suas próprias classes de exceção. Para um exemplo, consulte Como: Criar exceções definida pelo usuário.

  • Na maioria dos casos, use os tipos de exceção pré-definidos. Defina novos tipos de exceção somente para cenários programáticos. Apresente uma nova classe de exceção para permitir que um programador realize uma ação diferente em códigos com base na classe de exceção.

  • Para a maioria dos aplicativos, derive exceções personalizadas da classe Exception. Pensou-se originalmente que exceções personalizadas deveriam derivar da classe ApplicationException; no entanto, na prática isso não acresccentou valor significativo.

  • Inclua uma sequência de caracteres de descrição localizada em cada exceção. Quando o usuário vê uma mensagem de erro, ela é derivada da sequência de caracteres de descrição da exceção que foi lançada, em vez da classe de exceção.

  • Use mensagens de erro gramaticalmente corretas, incluindo a pontuação final. Cada sentença em uma sequência de caracteres descrição de uma exceção deve terminar com um ponto final.

  • Forneça propriedades Exception para acesso programático. Inclua informações extras em uma exceção (além da sequência de caracteres de descrição) somente quando há um cenário programático onde as informações adicionais são úteis.

  • Retorne null para casos extremamente comuns de erro. Por exemplo, Open retorna null se o arquivo não for encontrado, mas lança uma exceção se o arquivo estiver bloqueado.

  • Crie classes de modo que uma exceção nunca seja apresentada no uso normal. Por exemplo, uma FileStream classe expõe uma outra maneira de determinar se o fim do arquivo foi alcançado. Isso evita a exceção que é lançada se você ler depois do final do arquivo. O exemplo a seguir mostra como ler até o final do arquivo.

Class FileRead
    Public Sub ReadAll(fileToRead As FileStream)
        ' This if statement is optional
        ' as it is very unlikely that
        ' the stream would ever be null.
        If fileToRead Is Nothing Then
            Throw New System.ArgumentNullException()
        End If

        Dim b As Integer

        ' Set the stream position to the beginning of the file.
        fileToRead.Seek(0, SeekOrigin.Begin)

        ' Read each byte to the end of the file.
        For i As Integer = 0 To fileToRead.Length - 1
            b = fileToRead.ReadByte()
            Console.Write(b.ToString())
            ' Or do something else with the byte.
        Next i
    End Sub
End Class
class FileRead
{
    public void ReadAll(FileStream fileToRead)
    {
        // This if statement is optional
        // as it is very unlikely that
        // the stream would ever be null.
        if (fileToRead == null)
        {
            throw new System.ArgumentNullException();
        }

        int b;

        // Set the stream position to the beginning of the file.
        fileToRead.Seek(0, SeekOrigin.Begin);

        // Read each byte to the end of the file.
        for (int i = 0; i < fileToRead.Length; i++)
        {
            b = fileToRead.ReadByte();
            Console.Write(b.ToString());
            // Or do something else with the byte.
        }
    }
}
class FileRead
{
public:
    void ReadAll(FileStream^ fileToRead)
    {
        // This if statement is optional
        // as it is very unlikely that
        // the stream would ever be null.
        if (fileToRead == nullptr)
        {
            throw gcnew System::ArgumentNullException();
        }

        int b;

        // Set the stream position to the beginning of the file.
        fileToRead->Seek(0, SeekOrigin::Begin);

        // Read each byte to the end of the file.
        for (int i = 0; i < fileToRead->Length; i++)
        {
            b = fileToRead->ReadByte();
            Console::Write(b.ToString());
            // Or do something else with the byte.
        }
    }
};

Lançe um InvalidOperationException se uma propriedade de definição ou chamada de método não for adequada dado o estado atual do objeto.

Lançe um ArgumentException ou uma classe derivada de ArgumentException se forem passados parâmetros inválidos.

O rastreamento de pilha começa na instrução na qual a exceção é lançada e termina na instrução catch que captura a exceção. Esteja ciente desse fato quando decidir onde colocar uma instrução throw.

Use métodos de construtor de exceção. É comum uma classe lançar a mesma exceção a partir de locais diferentes em sua implementação. Para evitar código excessivo, use métodos auxiliares que criam a exceção e a retornam. Por exemplo:

Class FileReader
    Private fileName As String


    Public Sub New(path As String)
        fileName = path
    End Sub

    Public Function Read(bytes As Integer) As Byte()
        Dim results() As Byte = FileUtils.ReadFromFile(fileName, bytes)
        If results Is Nothing
            Throw NewFileIOException()
        End If
        Return results
    End Function

    Function NewFileIOException() As FileReaderException
        Dim description As String = "My NewFileIOException Description"

        Return New FileReaderException(description)
    End Function
End Class
class FileReader
{
    private string fileName;

    public FileReader(string path)
    {
        fileName = path;
    }

    public byte[] Read(int bytes)
    {
        byte[] results = FileUtils.ReadFromFile(fileName, bytes);
        if (results == null)
        {
            throw NewFileIOException();
        }
        return results;
    }

    FileReaderException NewFileIOException()
    {
        string description = "My NewFileIOException Description";

        return new FileReaderException(description);
    }
}
ref class FileReader
{
private:
    String^ fileName;

public:
    FileReader(String^ path)
    {
        fileName = path;
    }

    array<Byte>^ Read(int bytes)
    {
        array<Byte>^ results = FileUtils::ReadFromFile(fileName, bytes);
        if (results == nullptr)
        {
            throw NewFileIOException();
        }
        return results;
    }

    FileReaderException^ NewFileIOException()
    {
        String^ description = "My NewFileIOException Description";

        return gcnew FileReaderException(description);
    }
};

Como alternativa, use a exceção do construtor para criar a exceção. Isso é mais apropriado para classes de exceção globais, como ArgumentException.

  • Lançe exceções em vez de retornar um código de erro ou HRESULT.

  • Limpe resultados intermediários ao lançar uma exceção. Os chamadores devem ser capazes de pressupor que não haja efeitos colaterais quando uma exceção for lançada a partir de um método.

Consulte também

Conceitos

Tratamento e lançamento de exceções