Propositadamente levantando exceções para usar capturas


10

Para um típico if...elseempacotado com manipulação de exceção, é algo como o exemplo a seguir uma prática recomendada para evitar a duplicação de código?

try
{
    if (GetDataFromServer())
    {
        return ProcessData();
    }
    else
    {
        throw new Exception();
    }
catch(Exception ex)
{
    return null;
}

ao invés de...

try
{
    if (GetDataFromServer())
    {
        return ProcessData();
    }
    else
    {
        return null;
    }
}
catch(Exception ex)
{
    return null;
}

Sei que há um pequeno impacto no desempenho, mas estou me perguntando se isso é considerado uma prática aceitável. Atualmente, faço o segundo método - especialmente nos casos em que preciso lidar com exceções específicas de maneira diferente - mas fiquei pensando se o primeiro método é apropriado para casos simples.


se o método for pequeno o suficiente, removerei o else e retornarei nulo fora do bloco trycatch, para que eu precise retornar nulo apenas uma vez.
Fabio Marcolini

Respostas:


12

O uso de manipulação de exceção para controle de fluxo é desencorajado pela Microsoft.

E uma mesa redonda sobre o tema está disponível.

Dito isto, o C # suporta isso, e suponho que depende da condição encontrada se uma exceção é a resposta mais apropriada.


1
Parece-me que esse tipo de coisa está se esforçando muito para não usar eventos.
Radarbob

@radarbob: Como os eventos estão relacionados a isso?

@grovesNL - Lançando uma exceção em um ponto específico para chamar um determinado método em um bloco catch? Quacks como um evento para mim.
Radarbob

@radarbob: Não é um evento. Existem muitos casos de uso de amostra em que isso seria usado, conforme discutido no link da mesa redonda da resposta.

1
@radarbob Apenas um pouco de esclarecimento, as exceções foram projetadas como uma maneira de sinalizar ao chamador que algo ocorreu que o método chamado não pode manipular. No entanto, um evento deve ser ouvido. Uma exceção é uma interrupção forçada do fluxo normal de um programa. Uma exceção não capturada fará com que o aplicativo inteiro seja interrompido.

6

O impacto no desempenho é provavelmente insignificante, conforme explicado nesta resposta .

Então, vamos com a ideia de que o desempenho não é um problema. Você está jogando System.Exception, apenas para mover a execução para a catchcláusula . Jogar um BadControlFlowThatShouldBeRewrittenExceptionprovavelmente seria um exagero.

Vamos quebrar isso. Nós temos:

  • Método GetDataFromServer(os nomes dos métodos devem ser PascalCase em C #), o que pode gerar uma exceção ou retornar a bool.
  • Se o resultado foi true, execute ProcessData.
  • Retornar nullcaso contrário.

Parece que o método em que esse código está escrito está simplesmente fazendo muitas coisas. GetDataFromServerretornando uma boolaparência de falha de design, eu esperaria que esse método retornasse os dados obtidos do servidor , alguns IEnumerable<SomeType>que contenham 0 ou mais itens - ou seja, o caminho feliz retorna n itens em que n> 0 , não tão feliz path retorna 0 itens e o caminho infeliz explode com uma exceção não tratada, seja o que for.

Isso muda bastante a aparência do método - novamente, é difícil dizer se isso faz sentido, porque a postagem original tem apenas um ponto de saída (e, portanto, não seria compilado, pois nem todos os caminhos de código retornam um valor ), então este é apenas um palpite:

try
{
    var result = GetDataFromServer();
    return ProcessData(result);
}
catch
{
    return null;
}

Aqui você observaria ProcessDatae iteraria oe resultretornará nullse não houver nenhum item no IEnumerable.

Agora, por que o método está retornando null? O servidor estava inoperante? Existe um erro na consulta? A cadeia de conexão está usando as credenciais incorretas? Sempre que GetDataFromServerexplode com uma exceção que você não está esperando, você está engolindo, empurrando-a para debaixo do tapete e retornando um nullvalor. Eu recomendo capturar exceções específicas nesse caso e registrar todo o resto; a depuração será muito mais fácil dessa maneira.

Com uma catchcláusula geral que não captura a exceção, fica muito difícil diagnosticar qualquer coisa. Eu faria isso minimamente:

catch(Exception e)
{
    return null;
}

Agora você pode pelo menos quebrar e inspecionar ese as coisas derem errado.


TL; DR : Não, lançar e capturar exceções para controle de fluxo não é uma boa ideia.


Esta resposta demonstra exatamente por que tentei manter meu código genérico: não queria listar todas as exceções que realmente faço no meu código; Eu não queria listar os nomes dos métodos reais; Eu não queria listar a declaração do método; Eu não queria sugestões de sintaxe. Eu tinha uma pergunta única sobre a possibilidade de lançar exceções para controle de fluxo, o que foi prontamente respondido por B2K. Eu ficaria feliz em debater isso na meta.

3
Parece que isso deveria ter sido uma pergunta para os programadores. revisamos código, não idéias.
Malachi

2

na sua primeira resposta, há um impacto no desempenho que não precisa estar lá.

try
{
    if (GetDataFromServer())
    {
        return ProcessData();
    }
    else
    {
        throw new Exception();
    }
catch(Exception ex)
{
    return null;
}

quando você sai da instrução if para entrar na instrução Catch quando não precisa fazer o código mudar de direção, por assim dizer.

se você quiser return null; fazê-lo na instrução else, não em uma captura que é capturada após ser lançada da instrução else.

Provavelmente não se aplica ao seu código Real , mas se aplica ao código genérico que você forneceu.

Os padrões dizem que você não deve fazer isso.

Os padrões dizem que você deve fazer assim (novamente baseado no código genérico fornecido no OP)

if (GetDataFromServer())
{
    return ProcessData();
}
else
{
    Return null
}

e como você não tem nenhuma exceção específica que está capturando, não deve tentar aqui.

você deseja ver exceções quando elas ocorrerem, para que você possa corrigir o problema que cria a exceção.


1

Por que não o muito mais simples:

if (!GetDataFromServer()) return null;
ProcessData();

Se um manipulador de exceção existir, ele deve estar em ProcessData ()


Por que não gostaria de passar exceções ProcessData()para o nível superior?
grovesNL

@grovesNL Nada de útil estava sendo feito com exceção aqui.
precisa saber é o seguinte

1
Como assim? Se ProcessData()lança uma exceção agora, não é tratada. Eu quero que return nullneste nível se ProcessData()lança uma exceção, sem se modificar ProcessData().
GrovesNL
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.