Examine como criar e lançar exceções em C#
- 16 minutos
O .NET fornece uma hierarquia de classes de exceção que derivam da System.Exception classe base. Os aplicativos C# podem criar e lançar exceções de qualquer tipo de exceção. Os desenvolvedores também podem personalizar objetos de exceção com informações específicas do aplicativo atribuindo valores de propriedade.
Observação
Este módulo se concentra na criação e lançamento de exceções e na personalização de objetos de exceção. A criação de classes de exceção personalizadas está fora do escopo deste módulo.
Criar um objeto de exceção
Criar e lançar exceções de dentro do seu código é um aspeto importante da programação em C#. A capacidade de gerar uma exceção em resposta a uma condição, problema ou erro específico ajuda você a garantir a estabilidade do seu aplicativo.
O tipo de exceção que você cria depende do problema de codificação e deve corresponder o mais próximo possível da finalidade pretendida da exceção.
Por exemplo, suponha que você esteja criando um método chamado GraphData que executa a análise de dados. O método recebe uma matriz de dados como um parâmetro de entrada. O método espera que os dados de entrada estejam em um intervalo específico. Se o método recebe dados que estão fora do intervalo esperado, ele cria e lança uma exceção do tipo ArgumentException. A exceção será tratada em algum lugar abaixo da pilha de chamadas pelo código responsável pelo fornecimento dos dados.
Aqui estão alguns tipos de exceção comuns que você pode usar ao criar uma exceção:
-
ArgumentExceptionouArgumentNullException: Use esses tipos de exceção quando um método ou construtor é chamado com um valor de argumento inválido ou referência nula. -
InvalidOperationException: Use este tipo de exceção quando as condições operacionais de um método não suportarem a conclusão bem-sucedida de uma chamada de método específica. -
NotSupportedException: Use este tipo de exceção quando uma operação ou recurso não for suportado. -
IOException: Use este tipo de exceção quando uma operação de entrada/saída falhar. -
FormatException: Use este tipo de exceção quando o formato de uma cadeia de caracteres ou dados estiver incorreto.
A new palavra-chave é usada para criar uma instância de uma exceção. Por exemplo, você pode criar uma instância do ArgumentException tipo de exceção da seguinte maneira:
ArgumentException invalidArgumentException = new ArgumentException();
Configurar e lançar exceções personalizadas
O processo para lançar um objeto de 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.
Muitas vezes, é útil personalizar uma exceção com informações contextuais antes que ela seja lançada. Você pode fornecer informações específicas do aplicativo dentro de um objeto de exceção configurando suas propriedades. Por exemplo, o código a seguir cria um objeto de exceção nomeado invalidArgumentException com uma propriedade personalizada Message e, em seguida, lança a exceção:
ArgumentException invalidArgumentException = new ArgumentException("ArgumentException: The 'GraphData' method received data outside the expected range.");
throw invalidArgumentException;
Observação
A Message propriedade de uma exceção é de leitura única. Portanto, uma propriedade personalizada Message deve ser definida ao instanciar o objeto.
Ao personalizar um objeto de exceção, é importante fornecer mensagens de erro claras que descrevam o problema e como resolvê-lo. Você também pode incluir informações adicionais, como rastreamentos de pilha e códigos de erro para ajudar os usuários a corrigir o problema.
Um objeto de exceção também pode ser criado diretamente em uma throw instrução. Por exemplo:
throw new FormatException("FormatException: Calculations in process XYZ have been cancelled due to invalid data format.");
Algumas considerações a ter em mente ao lançar uma exceção incluem:
- A
Messagepropriedade deve explicar o motivo da exceção. No entanto, informações confidenciais ou que representem um problema de segurança não devem ser colocadas no texto da mensagem. - A
StackTracepropriedade é frequentemente usada para rastrear a origem da exceção. Essa propriedade string contém o nome dos métodos na pilha de chamadas atual, juntamente com o nome do arquivo e o número da linha em cada método associado à exceção. UmStackTraceobjeto é criado automaticamente pelo Common Language Runtime (CLR) a partir do ponto dathrowinstrução. As exceções devem ser lançadas a partir do ponto onde o rastreamento da pilha deve começar.
Quando lançar uma exceção
Os métodos devem lançar uma exceção sempre que não puderem completar o propósito pretendido. A exceção lançada deve ser baseada na exceção mais específica disponível que se encaixa nas condições de erro.
Considere um cenário em que um desenvolvedor está trabalhando em um aplicativo que implementa um processo de negócios. O processo de negócios depende da entrada do usuário. Se a entrada não corresponder ao tipo de dados esperado, o método que implementa o processo de negócios criará e lançará uma exceção. O objeto de exceção pode ser configurado com informações específicas do aplicativo nos valores de propriedade. O exemplo de código a seguir demonstra o cenário:
string[][] userEnteredValues = new string[][]
{
new string[] { "1", "two", "3"},
new string[] { "0", "1", "2"}
};
foreach (string[] userEntries in userEnteredValues)
{
try
{
BusinessProcess1(userEntries);
}
catch (Exception ex)
{
if (ex.StackTrace.Contains("BusinessProcess1") && (ex is FormatException))
{
Console.WriteLine(ex.Message);
}
}
}
static void BusinessProcess1(string[] userEntries)
{
int valueEntered;
foreach (string userValue in userEntries)
{
try
{
valueEntered = int.Parse(userValue);
// completes required calculations based on userValue
// ...
}
catch (FormatException)
{
FormatException invalidFormatException = new FormatException("FormatException: User input values in 'BusinessProcess1' must be valid integers");
throw invalidFormatException;
}
}
}
Neste exemplo de código, as instruções de nível superior chamam o BusinessProcess1 método, passando uma matriz de cadeia de caracteres que contém valores inseridos pelo usuário. O BusinessProcess1 método espera valores de entrada do usuário que podem ser convertidos em um inteiro. Quando o método encontra dados com um formato inválido, ele cria uma instância do FormatException tipo de exceção usando uma propriedade personalizada Message . O método em seguida lança a exceção. A exceção é capturada nas instruções de nível superior como um objeto chamado ex. As propriedades do objeto são examinadas antes de exibir a mensagem de ex exceção para o usuário. Primeiro, o código examina a StackTrace propriedade para ver se ela contém "BusinessProcess1". Em segundo lugar, verifica-se que o objeto ex de exceção é do tipo FormatException.
Relançamento de exceções
Além de lançar uma nova exceção, throw pode ser usado relançar uma exceção de dentro de um catch bloco de código. Nesse caso, throw não usa um operando de exceção.
catch (Exception ex)
{
// handle or partially handle the exception
// ...
// re-throw the original exception object for further handling down the call stack
throw;
}
Quando você relança uma exceção, o objeto de exceção original é usado, para que você não perca nenhuma informação sobre a exceção. Se você quiser criar um novo objeto de exceção que encapsula a exceção original, você pode passar a exceção original como um argumento para o construtor de um novo objeto de exceção. Por exemplo:
catch (Exception ex)
{
// handle or partially handle the exception
// ...
// create a new exception object that wraps the original exception
throw new ApplicationException("An error occurred", ex);
}
Para o cenário de aplicativo "BusinessProcess1", considere as seguintes atualizações:
- O
BusinessProcess1método foi atualizado para incluir detalhes adicionais.BusinessProcess1agora encontra dois problemas e deve gerar exceções para cada problema. - As declarações de nível superior foram atualizadas. As instruções de nível superior agora chamam o método
OperatingProcedure1.OperatingProcedure1chamaBusinessProcess1dentro de um bloco de códigotry. - O
OperatingProcedure1método é capaz de lidar com um dos tipos de exceção e lidar parcialmente com o outro. Uma vez que a exceção parcialmente tratada é processada,OperatingProcedure1deve relançar a exceção original.
O exemplo de código a seguir demonstra o cenário atualizado:
try
{
OperatingProcedure1();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Exiting application.");
}
static void OperatingProcedure1()
{
string[][] userEnteredValues = new string[][]
{
new string[] { "1", "two", "3"},
new string[] { "0", "1", "2"}
};
foreach(string[] userEntries in userEnteredValues)
{
try
{
BusinessProcess1(userEntries);
}
catch (Exception ex)
{
if (ex.StackTrace.Contains("BusinessProcess1"))
{
if (ex is FormatException)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Corrective action taken in OperatingProcedure1");
}
else if (ex is DivideByZeroException)
{
Console.WriteLine(ex.Message);
Console.WriteLine("Partial correction in OperatingProcedure1 - further action required");
// re-throw the original exception
throw;
}
else
{
// create a new exception object that wraps the original exception
throw new ApplicationException("An error occurred - ", ex);
}
}
}
}
}
static void BusinessProcess1(string[] userEntries)
{
int valueEntered;
foreach (string userValue in userEntries)
{
try
{
valueEntered = int.Parse(userValue);
checked
{
int calculatedValue = 4 / valueEntered;
}
}
catch (FormatException)
{
FormatException invalidFormatException = new FormatException("FormatException: User input values in 'BusinessProcess1' must be valid integers");
throw invalidFormatException;
}
catch (DivideByZeroException)
{
DivideByZeroException unexpectedDivideByZeroException = new DivideByZeroException("DivideByZeroException: Calculation in 'BusinessProcess1' encountered an unexpected divide by zero");
throw unexpectedDivideByZeroException;
}
}
}
O código de exemplo atualizado produz a seguinte saída:
FormatException: User input values in 'BusinessProcess1' must be valid integers
Corrective action taken in OperatingProcedure1
DivideByZeroException: Calculation in 'BusinessProcess1' encountered an unexpected divide by zero
Partial correction in OperatingProcedure1 - further action required
DivideByZeroException: Calculation in 'BusinessProcess1' encountered an unexpected divide by zero
Exiting application.
Coisas a evitar ao lançar exceções
A lista a seguir identifica as práticas a serem evitadas ao lançar exceções:
- Não use exceções para alterar o fluxo de um programa como parte da execução comum. Use exceções para relatar e lidar com condições de erro.
- As exceções não devem ser retornadas como um valor ou parâmetro de retorno em vez de serem lançadas.
- Não lance intencionalmente
System.Exception,System.SystemException,System.NullReferenceExceptionouSystem.IndexOutOfRangeExceptiona partir do seu próprio código-fonte. - Não crie exceções que possam ser lançadas no modo de depuração, mas não no modo de liberação. Para identificar erros de tempo de execução durante a fase de desenvolvimento, use
Debug.Assertem vez disso.
Observação
O Debug.Assert método é uma ferramenta para capturar erros de lógica durante o desenvolvimento. Por padrão, o Debug.Assert método funciona apenas em compilações de depuração. Você pode usar Debug.Assert em sessões de depuração para verificar uma condição que nunca deve ocorrer. O método usa dois parâmetros: uma condição booleana para verificar e uma mensagem de cadeia de caracteres opcional para exibir se a condição for false.
Debug.Assert não deve ser usado no lugar de lançar uma exceção, que é uma maneira de lidar com situações excecionais durante a execução normal do seu código. Você deve usar Debug.Assert para capturar erros que nunca devem ocorrer e usar exceções para lidar com erros que podem ocorrer durante a execução normal do seu programa.
Recapitulação
Aqui estão algumas coisas importantes para lembrar desta unidade:
- Ao criar e lançar uma exceção, o tipo de exceção deve corresponder o mais possível à finalidade pretendida da exceção.
- Para
throwuma exceção, crias uma instância de uma classe derivada de exceção, configuras as suas propriedades e, em seguida, usas athrowpalavra-chave. - Ao criar um objeto de exceção, é importante fornecer mensagens de erro claras e informações adicionais para ajudar os usuários a corrigir o problema.