o que é melhor, usando um anulável ou um retorno boolean + out parâmetro

StackOverflow https://stackoverflow.com/questions/1800003

  •  05-07-2019
  •  | 
  •  

Pergunta

Vamos dizer que eu tenho uma função que precisa retornar algum valor inteiro. mas também pode falhar, e eu preciso saber quando isso acontece.

Qual é a melhor maneira?

public int? DoSomethingWonderful()

ou

public bool DoSomethingWonderful(out int parameter)

este é provavelmente mais uma questão de estilo, mas eu ainda estou curioso qual opção pessoas tomariam.

Editar: esclarecimento, este fala de código para uma caixa preta (vamos chamá-lo de uma nuvem não, uma caixa preta não, espera nuvem sim....). Eu não me importo por falhou. Eu só preciso saber se eu tiver um valor válido ou não.

Foi útil?

Solução

Eu gosto da versão anulável melhor, porque você pode usar o operador nulo coalesce ?? sobre ele, por exemplo:.

int reallyTerrible = 0;
var mightBeWonderful = DoSomethingWonderful() ?? reallyTerrible;

Outras dicas

Depende de como você acha que o código de chamada deve ser parecida. E, portanto, que a função é usada.

Geralmente, você deve evitar a argumentos. Por outro lado, poderia ser bom ter um código como este:

int parameter;
if (DoSomething(out paramameter))
{
  // use parameter
}

Quando você tem um int anulável, ele ficaria assim:

int? result = DoSomething();
if (result != null)
{
  // use result
}

Este é um pouco melhor, porque você não tem um argumento, mas o código que decide se a função sucedido não parece muito óbvio.

Não se esqueça que não há outra opção: uso exeptions. Só faça isso se o caso em que a sua função não é realmente uma excepcional e tipo de um caso de erro.

try
{
  // normal case
  int result = DoSomething()
}
catch (SomethingFailedException ex)
{
  // exceptional case
}

Uma das vantagens da exceção é que você não pode simplesmente ignorá-lo. O caso normal também é simples de implementar. Se o caso excepcional algo que você pode ignorar, você não deve usar exceções.

Editar: esqueci de mencionar: outra vantagem de uma exceção é que você também pode fornecer informações por a operação falhou. Esta informação é fornecida pelo tipo de exceção, as propriedades da exceção eo texto da mensagem.

Por que não lançar uma exceção?

Gostaria de seguir o padrão usado em algum lugar na biblioteca .Net como:

bool int.TryParse(string s, out value)
bool Dictionary.TryGetValue(T1 key, out T2 value)

Então, eu diria:

public bool TryDoSomethingWonderful(out int parameter)

Ela realmente depende do que você está fazendo.

é nulo uma resposta significativa? Se não, eu preferiria uma chamada de método bool TryDoSomethingWonderful(out int). Isso combina com o quadro.

Se, no entanto, null é um valor de retorno significativo, retornando int? faz sentido.

A menos que o desempenho é a principal preocupação que você deve retornar um int e lançar uma exceção em caso de falha.

Gostaria de usar a segunda, porque eu provavelmente precisa saber imediatamente se a chamar teve êxito, e, nesse caso, eu preferiria escrever

int x;
if( DoSomethingWonderful( out x ) )
{
    SomethingElse(x);
}

de

int? x = DoSomethingWonderful();
if( x.HasValue )
{
   SomethingElse(x.Value);
}

Sou a favor de usar um parâmetro de saída. Na minha opinião, este é o tipo de situação para a qual o uso de um parâmetros de saída é mais adequado.

Sim, você pode usar o operador de coligar para manter seu código como um one-liner se e somente se você tem um valor alternativo que você pode usar no resto do seu código. Muitas vezes eu achar que não é o caso para mim, e eu preferiria executar um caminho de código diferente do que eu faria se eu estava com sucesso capaz de recuperar um valor.

        int value;
        if(DoSomethingWonderful(out value))
        {
            // continue on your merry way
        }
        else
        {
            // oops
            Log("Unable to do something wonderful");

            if (DoSomethingTerrible(out value))
            {
                // continue on your not-so-merry way
            }
            else
            {
                GiveUp();
            }
        }

Além disso, se o valor que eu quero recuperar é realmente nulo, em seguida, usando uma função com um parâmetro de saída e um valor de retorno boolean é, na minha opinião, a maneira mais fácil de dizer a diferença entre "eu não teve sucesso em recuperar o valor" e 'o valor I recuperado é nulo'. Às vezes eu me preocupo com essa distinção, como no exemplo a seguir:

    private int? _Value;
    private bool _ValueCanBeUsed = false;

    public int? Value
    {
        get { return this._Value; }
        set
        {
            this._Value = value;
            this._ValueCanBeUsed = true;
        }
    }

    public bool DoSomethingTerrible(out int? value)
    {
        if (this._ValueCanBeUsed)
        {
            value = this._Value;
            // prevent others from using this value until it has been set again
            this._ValueCanBeUsed = false;
            return true;
        }
        else
        {
            value = null;
            return false;
        }
    }

Na minha opinião, a única razão pela qual a maioria das pessoas tendem a não usar parâmetros de saída é porque eles encontram o incómodo de sintaxe. No entanto, eu realmente sinto que o uso de parâmetros de saída é a solução mais adequada para este problema, e eu achei que uma vez eu me acostumei com isso eu encontrei a sintaxe muito mais preferível retornar um valor nulo.

Se há apenas uma maneira que pode falhar, ou se você nunca mais vai precisar saber por ele falhou, eu diria que é provavelmente mais simples e mais fácil de ir com o valor de retorno nulo.

Por outro lado, se houver várias maneiras poderia falhar, e o código de chamada poderia querer saber exatamente o motivo da falha, então vá com o parâmetro de saída e retornar um código de erro em vez de um booleano (alternativamente, você poderia lançar uma exceção , mas com base na sua pergunta, parece que você já decidiu não lançar uma exceção).

Você deve, antes, em seguida, usar um try catch. Este parece ser o chamador não sabe o que pode acontecer?

Devemos verificar tanto bool eo fora, ou eu deveria verificar ambos os retornos nulo eo retorno real.

Deixe o método de fazer o que deveria, e no caso em que ele falhou, deixe o know chamador que ele falhou, eo hanlde chamador como requied.

Curiosamente, meus balanços de opinião pessoal significativamente com base na natureza do método. Especificamente, se o método objetivo é recuperar um único valor , como se opondo a "fazer alguma coisa".

Ex:

bool GetSerialNumber(out string serialNumber)

vs

string GetSerialNumber() // returns null on failure

O segundo se sente mais "natural" para mim de alguma forma, e da mesma forma:

bool GetDeviceId(out int id)

vs

int? GetDeviceId() // returns null on failure`

Mas admito isso realmente cai em território "estilo de codificação".

Oh, e eu também tenderia a favorecer arremesso exceção:

int GetDeviceId() // throws an exception on read failure

Estou ainda não vendido sobre o porquê eles seria tão errado. Podemos ter uma discussão sobre isso, Oren? ; -)

Eu não gosto de padrão "Experimente" da Microsoft em que o "fora" parâmetro é usado para retornar um item de dados. Entre outras coisas, os métodos codificados em que a moda não pode ser usado em interfaces de covariantes. Eu preferiria ver um método codificado como: T GetValue(out bool Successful) ou talvez T GetValue(out GetValueErrorEnum result); ou T GetValue(out GetValueErrorInfo result); se algo além de um verdadeiro / falso pode ser necessário. Uma vez que cada tipo de dados tem um valor padrão legal, não há nenhum problema com a decidir o que retornar se a função falhar. código de chamada pode facilmente dizer:

  bool success;
  var myValue = Thing.GetValue(ref success);
  if (success)
    .. do something using myValue
  else
    .. ignore myValue

Seria bom se .NET e C # oferecido verdadeira covariant 'copiar out' parâmetros (o chamador iria alocar espaço para o resultado, passar um ponteiro para que o espaço para a função de chamada, e depois copiar o espaço alocado para o passado variável -em apenas após a função retornado).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top