exception - tratamento - try catch c# macoratti




Quando lançar uma exceção? (20)

A principal razão para evitar lançar uma exceção é que há muita sobrecarga envolvida em lançar uma exceção.

Uma coisa que o artigo abaixo afirma é que uma exceção é por condições e erros excepcionais.

Um nome de usuário incorreto não é necessariamente um erro de programa, mas um erro do usuário ...

Aqui está um ponto de partida decente para exceções no .NET: http://msdn.microsoft.com/en-us/library/ms229030(VS.80).aspx

Eu tenho exceções criadas para todas as condições que meu aplicativo não espera. UserNameNotValidException , PasswordNotCorrectException etc.

No entanto, disseram-me que não deveria criar exceções para essas condições. Na minha UML, essas são exceções para o fluxo principal, então por que não deveria ser uma exceção?

Alguma orientação ou melhores práticas para criar exceções?


A resposta simples é, sempre que uma operação é impossível (por causa de qualquer aplicativo OU porque violaria a lógica de negócios). Se um método for invocado e for impossível fazer o que o método foi escrito, faça uma exceção. Um bom exemplo é que os construtores sempre lançam ArgumentExceptions se uma instância não puder ser criada usando os parâmetros fornecidos. Outro exemplo é InvalidOperationException, que é lançado quando uma operação não pode ser executada devido ao estado de outro membro ou membros da classe.

No seu caso, se um método como Login (nome de usuário, senha) for invocado, se o nome de usuário não for válido, é realmente correto lançar uma UserNameNotValidException ou PasswordNotCorrectException se a senha estiver incorreta. O usuário não pode estar logado usando o (s) parâmetro (s) fornecido (s) (ou seja, é impossível porque violaria a autenticação), portanto, lance uma exceção. Embora eu possa ter suas duas exceções herdadas de ArgumentException.

Dito isto, se você deseja NÃO lançar uma Exceção porque uma falha de login pode ser muito comum, uma estratégia é criar um método que retorna tipos que representam falhas diferentes. Aqui está um exemplo:

{ // class
    ...

    public LoginResult Login(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            return new UserInvalidLoginResult(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            return new PasswordInvalidLoginResult(user, password);
        }
        else
        {
            return new SuccessfulLoginResult();
        }
    }

    ...
}

public abstract class LoginResult
{
    public readonly string Message;

    protected LoginResult(string message)
    {
        this.Message = message;
    }
}

public class SuccessfulLoginResult : LoginResult
{
    public SucccessfulLogin(string user)
        : base(string.Format("Login for user '{0}' was successful.", user))
    { }
}

public class UserInvalidLoginResult : LoginResult
{
    public UserInvalidLoginResult(string user)
        : base(string.Format("The username '{0}' is invalid.", user))
    { }
}

public class PasswordInvalidLoginResult : LoginResult
{
    public PasswordInvalidLoginResult(string password, string user)
        : base(string.Format("The password '{0}' for username '{0}' is invalid.", password, user))
    { }
}

A maioria dos desenvolvedores é ensinada a evitar exceções por causa da sobrecarga causada por lançamentos. É ótimo estar consciente dos recursos, mas geralmente não à custa do design do seu aplicativo. Essa é provavelmente a razão pela qual lhe disseram para não lançar suas duas Exceções. Se usar Exceções ou não, geralmente, reduz a frequência com que a Exceção ocorrerá. Se for um resultado bastante comum ou bastante esperado, é quando a maioria dos desenvolvedores evitará exceções e criará outro método para indicar falha, devido ao suposto consumo de recursos.

Aqui está um exemplo de evitar o uso de exceções em um cenário como o descrito acima, usando o padrão Try ():

public class ValidatedLogin
{
    public readonly string User;
    public readonly string Password;

    public ValidatedLogin(string user, string password)
    {
        if (IsInvalidUser(user))
        {
            throw new UserInvalidException(user);
        }
        else if (IsInvalidPassword(user, password))
        {
            throw new PasswordInvalidException(password);
        }

        this.User = user;
        this.Password = password;
    }

    public static bool TryCreate(string user, string password, out ValidatedLogin validatedLogin)
    {
        if (IsInvalidUser(user) || 
            IsInvalidPassword(user, password))
        {
            return false;
        }

        validatedLogin = new ValidatedLogin(user, password);

        return true;
    }
}

Ao lançar exceções, a pilha pode ser desfeita, o que causa alguns impactos no desempenho (admitiu que os ambientes gerenciados modernos melhoraram). Ainda repetidamente jogando e pegando exceções em uma situação aninhada seria uma má idéia.

Provavelmente mais importante do que isso, as exceções são destinadas a condições excepcionais. Eles não devem ser usados ​​para fluxo de controle comum, porque isso prejudicará a legibilidade do seu código.


As exceções são destinadas a eventos que são comportamentos anormais, erros, falhas e outros. Comportamento funcional, erro do usuário, etc., devem ser manipulados pela lógica do programa. Como uma conta ou senha incorreta é uma parte esperada do fluxo lógico em uma rotina de login, ela deve ser capaz de lidar com essas situações sem exceções.


Em geral, você quer lançar uma exceção para qualquer coisa que possa acontecer em seu aplicativo que seja "Excepcional"

No seu exemplo, ambas as exceções parecem que você está chamando-as por meio de uma validação de senha / nome de usuário. Nesse caso, pode-se argumentar que não é realmente excepcional que alguém digitaria um nome de usuário / senha incorretamente.

Eles são "exceções" para o fluxo principal de sua UML, mas são mais "ramificações" no processamento.

Se você tentou acessar seu arquivo ou banco de dados passwd e não conseguiu, isso seria um caso excepcional e autorizaria uma exceção.


Em primeiro lugar, se os usuários de sua API não estiverem interessados ​​em falhas específicas e refinadas, então, ter exceções específicas para eles não terá qualquer valor.

Como muitas vezes não é possível saber o que pode ser útil para seus usuários, uma abordagem melhor é ter as exceções específicas, mas garantir que elas herdem de uma classe comum (por exemplo, std :: exception ou suas derivadas em C ++). Isso permite que seu cliente detecte exceções específicas, ou a exceção mais geral, caso não se importe.


Eu concordo com o japollock lá em cima - lance uma aceitação quando você está incerto sobre o resultado de uma operação. Chamadas para APIs, acesso a sistemas de arquivos, chamadas de banco de dados, etc. Sempre que você estiver passando dos "limites" de suas linguagens de programação.

Eu gostaria de adicionar, fique à vontade para lançar uma exceção padrão. A menos que você vá fazer algo "diferente" (ignorar, enviar e-mail, logar, mostrar aquela imagem da baleia no twitter, etc), então não se preocupe com exceções personalizadas.


Eu diria que geralmente todo fundamentalismo leva ao inferno.

Você certamente não desejaria acabar com o fluxo acionado por exceção, mas evitar exceções também é uma má ideia. Você tem que encontrar um equilíbrio entre as duas abordagens. O que eu não faria é criar um tipo de exceção para cada situação excepcional. Isso não é produtivo.

O que eu geralmente prefiro é criar dois tipos básicos de exceções que são usadas em todo o sistema: LogicalException e TechnicalException . Estes podem ainda ser distinguidos por subtipos, se necessário, mas geralmente não é necessário.

A exceção técnica denota a exceção realmente inesperada como o servidor de banco de dados estar inativo, a conexão com o serviço da web lançou o IOException e assim por diante.

Por outro lado, as exceções lógicas são usadas para propagar a situação errônea menos grave para as camadas superiores (geralmente algum resultado de validação).

Observe que mesmo a exceção lógica não deve ser usada regularmente para controlar o fluxo do programa, mas sim para realçar a situação em que o fluxo deve realmente terminar. Quando usados ​​em Java, ambos os tipos de exceção são subclasses de RuntimeException e o tratamento de erros é altamente orientado a aspectos.

Portanto, no exemplo de login, pode ser sábio criar algo como AuthenticationException e distinguir as situações concretas por valores enum como UsernameNotExisting , PasswordMismatch etc. Então você não vai acabar tendo uma hierarquia de exceções enorme e pode manter os blocos catch em nível de manutenção . Você também pode empregar facilmente algum mecanismo genérico de tratamento de exceções, desde que tenha as exceções categorizadas e saiba muito bem o que propagar para o usuário e como.

Nosso uso típico é lançar a LogicalException durante a chamada de serviço da Web quando a entrada do usuário é inválida. A Exceção é organizada para o detalhe SOAPFault e, em seguida, fica desmarcada para a exceção novamente no cliente, o que resulta na exibição do erro de validação em um determinado campo de entrada da página da Web, pois a exceção possui o mapeamento adequado para esse campo.

Essa certamente não é a única situação: você não precisa acessar o serviço da Web para lançar a exceção. Você é livre para fazê-lo em qualquer situação excepcional (como no caso de você precisar falhar rápido) - é tudo a seu critério.


Eu tenho problemas filosóficos com o uso de exceções. Basicamente, você está esperando que um cenário específico ocorra, mas, em vez de manipulá-lo explicitamente, você está empurrando o problema para ser tratado "em outro lugar". E onde esse "outro lugar" é, pode-se imaginar.


Eu tenho três tipos de condições que eu pego.

  1. Entrada incorreta ou ausente não deve ser uma exceção. Use tanto js do lado do cliente e regex do lado do servidor para detectar, definir atributos e voltar para a mesma página com mensagens.

  2. O AppException. Isso geralmente é uma exceção que você detecta e joga no seu código. Em outras palavras, essas são as que você espera (o arquivo não existe). Registre-o, defina a mensagem e volte para a página de erro geral. Esta página geralmente tem um pouco de informação sobre o que aconteceu.

  3. A exceção inesperada. Estes são os que você não conhece. Registre com detalhes e encaminhe-os para uma página de erro geral.

Espero que isto ajude


Minha orientação pessoal é: uma exceção é lançada quando uma suposição fundamental do bloco de código atual é considerada falsa.

Exemplo 1: digamos que eu tenha uma função que deve examinar uma classe arbitrária e retornar true se essa classe herdar de List <>. Esta função faz a pergunta: "Este objeto é um descendente de List?" Esta função nunca deve lançar uma exceção, porque não há áreas cinzas em sua operação - cada classe herda ou não da List <>, então a resposta é sempre "yes" ou "no".

Exemplo 2: digamos que eu tenha outra função que examine uma List <> e retorne true se seu comprimento for maior que 50, e false se o comprimento for menor. Esta função faz a pergunta: "Esta lista tem mais de 50 itens?" Mas esta questão faz uma suposição - assume que o objeto que é dado é uma lista. Se eu entrego um NULL, então essa suposição é falsa. Nesse caso, se a função retornar true ou false, ela estará violando suas próprias regras. A função não pode retornar nada e afirma que respondeu a pergunta corretamente. Por isso não retorna - lança uma exceção.

Isso é comparável à falácia lógica da "pergunta carregada" . Cada função faz uma pergunta. Se a entrada que é dada faz com que essa pergunta seja uma falácia, então lance uma exceção. Esta linha é mais difícil de desenhar com funções que retornam void, mas o resultado final é: se as suposições da função sobre suas entradas forem violadas, ela deve lançar uma exceção em vez de retornar normalmente.

O outro lado dessa equação é: se você encontrar suas funções emitindo exceções com frequência, provavelmente precisará refinar suas suposições.


Minhas pequenas diretrizes são fortemente influenciadas pelo ótimo livro "Código completo":

  • Use exceções para notificar sobre coisas que não devem ser ignoradas.
  • Não use exceções se o erro puder ser manipulado localmente
  • Certifique-se de que as exceções estejam no mesmo nível de abstração que o resto de sua rotina.
  • Exceções devem ser reservadas para o que é verdadeiramente excepcional .

Outros propõem que as exceções não devem ser usadas porque o login incorreto é esperado em um fluxo normal se o usuário digitar incorretamente. Eu discordo e não entendo o raciocínio. Compare-o com a abertura de um arquivo. Se o arquivo não existir ou não estiver disponível por algum motivo, uma exceção será lançada pela estrutura. Usando a lógica acima, isso foi um erro da Microsoft. Eles deveriam ter retornado um código de erro. O mesmo para análise, webrequests, etc., etc.

Eu não considero um login incorreto parte de um fluxo normal, é excepcional. Normalmente, o usuário digita a senha correta e o arquivo existe. Os casos excepcionais são excepcionais e é perfeitamente correto usar exceções para eles. Complicar seu código propagando valores de retorno através de n níveis acima da pilha é um desperdício de energia e resultará em um código confuso. Faça a coisa mais simples que poderia funcionar. Não otimize prematuramente usando códigos de erro, coisas excepcionais por definição raramente acontecem, e as exceções não custam nada a menos que você as jogue.


Porque são coisas que acontecem normalmente. Exceções não são mecanismos de fluxo de controle. Os usuários costumam ter senhas erradas, não é um caso excepcional. Exceções devem ser uma coisa realmente rara, situações do tipo UserHasDiedAtKeyboard .


Uma regra prática é usar exceções no caso de algo que você não poderia prever normalmente. Os exemplos são conectividade do banco de dados, arquivo ausente no disco, etc. Para cenários que você pode prever, ou seja, usuários que tentam efetuar login com uma senha incorreta, você deve usar funções que retornam booleanos e saber como lidar com a situação normalmente. Você não quer encerrar abruptamente a execução, lançando uma exceção apenas porque alguém digitou incorretamente sua senha.


a regra geral para lançar exceções é bem simples. você faz isso quando seu código entrou em um estado INVÁLIDO INCONVENÁVEL. Se os dados forem comprometidos ou você não puder retroceder o processamento que ocorreu até o ponto, será necessário finalizá-lo. Na verdade, o que mais você pode fazer? sua lógica de processamento eventualmente falhará em outro lugar. Se você pode recuperar de alguma forma, faça isso e não jogue exceção.

no seu caso particular, se você fosse forçado a fazer algo bobo, como aceitar a retirada do dinheiro e só então verificar o usuário / senha, você deveria terminar o processo lançando uma exceção para notificar que algo ruim aconteceu e evitar mais danos.


Existem duas classes principais de exceção:

1) Exceção do sistema (por exemplo, conexão do banco de dados perdida) ou 2) Exceção do usuário. (por exemplo, validação da entrada do usuário, 'senha incorreta')

Eu achei útil criar minha própria classe de exceção de usuário e quando eu quero lançar um erro de usuário eu quero ser tratado de forma diferente (ou seja, com erros de recursos exibidos para o usuário) então tudo que eu preciso fazer no meu manipulador de erro principal é verificar o tipo de objeto :

            If TypeName(ex) = "UserException" Then
               Display(ex.message)
            Else
               DisplayError("An unexpected error has occured, contact your help  desk")                   
               LogError(ex)
            End If

"PasswordNotCorrectException" não é um bom exemplo para usar exceções. Usuários recebendo suas senhas errado é de se esperar, por isso é dificilmente uma exceção IMHO. Você provavelmente se recupera, mostrando uma boa mensagem de erro, então é apenas uma verificação de validade.

Exceções não tratadas interromperão a execução eventualmente - o que é bom. Se você está retornando códigos falsos, nulos ou de erro, você terá que lidar com o estado do programa sozinho. Se você esquecer de verificar as condições em algum lugar, seu programa pode continuar sendo executado com dados errados, e você pode ter dificuldade em descobrir o que aconteceu e onde .

É claro, você poderia causar o mesmo problema com instruções catch vazias, mas pelo menos localizá-las é mais fácil e não requer que você entenda a lógica.

Então, como regra geral:

Use-os onde quer que você não queira ou simplesmente não consiga se recuperar de um erro.


Em minha opinião, a questão fundamental deveria ser se alguém esperaria que o chamador desejasse continuar o fluxo normal do programa se uma condição ocorresse. Se você não sabe, tenha métodos doSomething e trySomething separados, em que o primeiro retorna um erro e o último não, ou tem uma rotina que aceita um parâmetro para indicar se uma exceção deve ser lançada se falhar). Considere uma classe para enviar comandos para um sistema remoto e relatar respostas. Certos comandos (por exemplo, reiniciar) farão com que o sistema remoto envie uma resposta, mas não respondam por um determinado período de tempo. Portanto, é útil poder enviar um comando "ping" e descobrir se o sistema remoto responde em um período de tempo razoável sem ter que lançar uma exceção se ele não funcionar.t (o chamador provavelmente esperaria que as primeiras tentativas de "ping" falhassem, mas uma acabaria por funcionar). Por outro lado, se alguém tiver uma sequência de comandos como:

  exchange_command("open tempfile");
  exchange_command("write tempfile data {whatever}");
  exchange_command("write tempfile data {whatever}");
  exchange_command("write tempfile data {whatever}");
  exchange_command("write tempfile data {whatever}");
  exchange_command("close tempfile");
  exchange_command("copy tempfile to realfile");

alguém poderia querer que a falha de qualquer operação abortasse toda a sequência. Embora seja possível verificar cada operação para garantir que ela seja bem-sucedida, é mais útil ter a rotina exchange_command () lançando uma exceção se um comando falhar.

Na verdade, no cenário acima pode ser útil ter um parâmetro para selecionar vários modos de tratamento de falhas: nunca lançar exceções, lançar exceções apenas para erros de comunicação ou lançar exceções em qualquer caso em que um comando não retorne um "sucesso" "indicação.


Você pode usar algumas exceções genéricas para essas condições. Por exemplo, ArgumentException deve ser usado quando algo der errado com os parâmetros para um método (com exceção de ArgumentNullException). Geralmente você não precisaria de exceções como LessThanZeroException, NotPrimeNumberException etc. Pense no usuário do seu método. O número de condições que ela desejará manipular especificamente é igual ao número do tipo de exceções que seu método precisa lançar. Dessa forma, você pode determinar quantas exceções detalhadas você terá.

By the way, sempre tente fornecer algumas maneiras para os usuários de suas bibliotecas para evitar exceções. TryParse é um bom exemplo, existe para que você não precise usar int.Parse e capturar uma exceção. No seu caso, você pode querer fornecer alguns métodos para verificar se o nome de usuário é válido ou se a senha está correta para que seus usuários (ou você) não precisem fazer muitas manipulações de exceção. Espero que isso resulte em código mais legível e melhor desempenho.





language-agnostic