Quero saber o que é considerado a melhor maneira de retornar quando tenho uma if
declaração.
Exemplo 1:
public bool MyFunction()
{
// Get some string for this example
string myString = GetString();
if (myString == null)
{
return false;
}
else
{
myString = "Name " + myString;
// Do something more here...
return true;
}
}
Exemplo 2:
public bool MyFunction()
{
// Get some string for this example
string myString = GetString();
if (myString == null)
{
return false;
}
myString = "Name " + myString;
// Do something more here...
return true;
}
Como você pode ver nos dois exemplos, a função retornará, true/false
mas é uma boa ideia colocar uma else
declaração como no primeiro exemplo ou é melhor não colocá-la?
else
.Respostas:
O exemplo 2 é conhecido como bloco de proteção. É mais adequado retornar / lançar a exceção mais cedo se algo der errado (parâmetro errado ou estado inválido). No fluxo lógico normal, é melhor usar o Exemplo 1
fonte
Meu estilo pessoal é usar o single
if
para blocos de proteção e oif
/else
no código de processamento do método real.Nesse caso, você está usando a
myString == null
condição de guarda, então eu tenderia a usar oif
padrão único .Considere o código um pouco mais complicado:
Exemplo 1:
Exemplo 2:
No exemplo 1, a proteção e o restante do método estão no formato
if
/else
. Compare isso ao Exemplo 2, onde o bloco de proteção está noif
formato único , enquanto o restante do método usa oif
/else
formulário. Pessoalmente, acho o exemplo 2 mais fácil de entender, enquanto o exemplo 1 parece confuso e com recuo excessivo.Observe que este é um exemplo artificial e que você pode usar
else if
instruções para limpá-lo, mas pretendo mostrar a diferença entre os blocos de proteção e o código de processamento da função real.Um compilador decente deve gerar a mesma saída para os dois de qualquer maneira. O único motivo para usar um ou outro é a preferência pessoal ou a conformidade com o estilo do código existente.
fonte
pessoalmente, eu prefiro o segundo método. Sinto que é mais curto, tem menos recuo e é mais fácil de ler.
fonte
Minha prática pessoal é a seguinte:
Por exemplo:
Por exemplo:
A regra "uma saída" também ajuda quando o cálculo requer recursos externos que você precisa liberar ou afirma que é necessário redefinir antes de sair da função; às vezes, são adicionados posteriormente durante o desenvolvimento. Com várias saídas dentro do algoritmo, é muito mais difícil estender todas as ramificações corretamente. (E, se houver exceções, a liberação / redefinição também deve ser bloqueada para evitar efeitos colaterais em casos excepcionais raros ...).
Seu caso parece se encaixar na categoria "saída rápida antes do trabalho real", e eu o escreveria como sua versão do Exemplo 2.
fonte
Prefiro empregar blocos de guarda sempre que possível por dois motivos:
De um modo geral, prefiro ver métodos em que a funcionalidade principal do método é clara e mínima. Blocos de proteção ajudam a fazer isso acontecer visualmente.
fonte
Gosto da abordagem "Fall Through":
A ação tem uma condição específica; qualquer outra coisa é apenas o retorno "fall through" padrão.
fonte
Se eu tiver uma condição única, não gastaria muito tempo pensando sobre o estilo. Mas se eu tiver várias condições de guarda, prefiro o style2
Picuture isso. Suponha que os testes sejam complexos e você realmente não deseja vinculá-los a uma única condição if-ORed para evitar a complexidade:
versus
Aqui existem duas vantagens principais
Você está separando o código em uma única função em dois blocos visualmente logais: um bloco superior de validações (condições de guarda) e um bloco inferior de código executável.
Se você precisar adicionar / remover uma condição, reduz as chances de estragar toda a escada if-elseif-else.
Outra pequena vantagem é que você tem um conjunto a menos de aparelhos para cuidar.
fonte
Isso deve ser uma questão de que soa melhor.
se expressa melhor então
para métodos pequenos, não haverá muita diferença, mas para métodos compostos maiores, pode ser mais difícil de entender, pois não será adequadamente separado.
fonte
do something
incluir um retorno. Caso contrário, o segundo código será executadodo somethingelse
independentemente doif
predicado, que não é como o primeiro bloco funciona.Acho o exemplo 1 irritante, porque a declaração de retorno ausente no final de uma função de retorno de valor dispara imediatamente o sinalizador "Aguarde, há algo errado". Então, em casos como esse, eu iria com o exemplo 2.
No entanto, geralmente há mais envolvimento, dependendo do objetivo da função, por exemplo, tratamento de erros, registro etc. Então, eu estou com a resposta de Lorand Kedves sobre isso, e geralmente tenho um ponto de saída no final, mesmo com o custo de uma variável de flag adicional. Facilita a manutenção e as extensões posteriores na maioria dos casos.
fonte
Quando sua
if
instrução sempre retorna, não há razão para usar uma outra coisa para o código restante na função. Fazer isso adiciona linhas extras e recuo extra. A adição de código desnecessário torna mais difícil a leitura e, como todos sabem, o código de leitura é difícil .fonte
No seu exemplo, o
else
obviamente é desnecessário, MAS ...Ao percorrer rapidamente as linhas de código, os olhos normalmente olham para os aparelhos e os recuos para ter uma idéia do fluxo de código; antes que eles realmente se instalem no próprio código. Portanto, escrever
else
e colocar chaves e recuo em torno do segundo bloco de código torna mais rápido para o leitor ver que essa é uma situação "A ou B", na qual um bloco ou outro será executado. Ou seja, o leitor vê as chaves e o recuo antes de ver oreturn
depois da inicialif
.Na minha opinião, este é um daqueles casos raros em que adicionar algo redundante ao código realmente o torna mais legível, e não menos.
fonte
Eu prefiro o exemplo 2 porque é imediatamente óbvio que a função retorna algo . Mas mais do que isso, eu preferiria retornar de um lugar, assim:
Com este estilo eu posso:
Veja imediatamente que estou retornando algo.
Capture o resultado de cada chamada da função com apenas um ponto de interrupção.
fonte
Meu estilo pessoal tende a ir
Usando as
else
instruções comentadas para indicar o fluxo do programa e mantê-lo legível, mas evitando recuos massivos que podem chegar facilmente na tela se houver várias etapas envolvidas.fonte
else
houvesse uma escolha entre este e o código com as cláusulas incluídas, eu escolheria o último, porque o compilador também os ignorará. Embora isso dependa deelse if
não aumentar o recuo mais do que o original uma vez - se isso acontecer, eu concordo com você. Mas minha escolha seria abandonarelse
completamente se esse estilo fosse permitido.eu escreveria
Eu prefiro esse estilo porque é mais óbvio que eu voltaria
userName != null
.fonte
myString
resultados não utilizados . Acabei de copiar a função do OP. Vou mudar para algo que parece mais real. O teste booleano é trivial e não deve ser problema.Minha regra de ouro é fazer um if-return sem outro (principalmente por erros) ou uma cadeia if-else-if completa sobre todas as possibilidades.
Dessa forma, evito tentar ser extravagante demais com minha ramificação, pois sempre que acabo fazendo isso codifico a lógica do aplicativo nos meus ifs, dificultando sua manutenção (por exemplo, se um enum está OK ou ERR e escrevo todos os meus ifs tirando proveito do fato de que! OK <-> ERR, torna-se uma chatice adicionar uma terceira opção ao enum da próxima vez.)
No seu caso, por exemplo, eu usaria um if simples, já que "return if null" certamente é um padrão comum e não é provável que você precise se preocupar com uma terceira possibilidade (além de null / not null) no futuro.
No entanto, se o teste fosse algo que fizesse uma inspeção mais aprofundada dos dados, eu iria errar no sentido de um if-else completo.
fonte
Penso que existem muitas armadilhas no Exemplo 2, que no futuro poderiam levar a códigos não intencionais. O primeiro do foco aqui é baseado na lógica em torno da variável 'myString'. Portanto, para ser explícito, todos os testes de condições devem ocorrer em um bloco de código que contabilize lógica conhecida e padrão / desconhecida .
E se, mais tarde, o código for introduzido acidentalmente no exemplo 2 que alterou significativamente a saída:
Eu acho que com o
else
bloco imediatamente após a verificação de um nulo, os programadores que adicionaram os aprimoramentos às versões futuras juntam toda a lógica, porque agora temos uma sequência de lógica não intencional para a regra original (retornando se o valor for nulo).Eu acredito muito nisso em algumas das diretrizes C # no Codeplex (link para isso aqui: http://csharpguidelines.codeplex.com/ ) que afirmam o seguinte:
"Adicione um comentário descritivo se o bloco padrão (else) estiver vazio. Além disso, se esse bloco não for atingido, lance uma InvalidOperationException para detectar alterações futuras que possam ocorrer nos casos existentes. Isso garante um código melhor, porque todos os caminhos que o código pode percorrer foram considerados ".
Eu acho que é uma boa prática de programação ao usar blocos lógicos como este, sempre adicionar um bloco padrão (caso contrário, case: default) para explicar explicitamente todos os caminhos de código e não deixar o código aberto para consequências lógicas não intencionais.
fonte
myString
valor e o código que sucede aoif
bloco é a lógica resultante se a sequência! = Null. Portanto, como o foco está na lógica adicional que manipula esse valor específico , acho que ele deve ser encapsulado em umelse
bloco. Caso contrário, isso abre potencial, como mostrei para separar involuntariamente a lógica e introduzir consequências não intencionais. Pode não acontecer o tempo todo, mas essa realmente era uma questão de estilo de opinião sobre práticas recomendadas .