Pergunta

Eu tenho vários métodos semelhantes, dizer por exemplo. CalculatePoint (...) e CalculateListOfPoints (...). Ocasionalmente, eles podem não ter sucesso, e precisa indicar isso para o chamador. Para CalculateListOfPoints, que retorna uma lista genérica, eu poderia retornar uma lista vazia e exigir que o chamador para verificar isso; no entanto Point é um tipo de valor e por isso não pode retornar nulo lá.

Idealmente, eu gostaria que os métodos para 'olhar' similar; Uma solução poderia ser a defini-los como

public Point CalculatePoint(... out Boolean boSuccess);
public List<Point> CalculateListOfPoints(... out Boolean boSuccess);

ou, alternativamente, para retornar um ponto? para CalculatePoint, e nulo retorno para indicar falha. Isso significaria ter de elenco de volta para o tipo não-nulo, porém, que parece excessivo.

Outro caminho seria para retornar o booleano boSuccess, ter o resultado (Ponto ou List) como parâmetro de 'fora', e chamá-los TryToCalculatePoint ou algo assim ...

O que é a melhor prática?

Edit: Eu não quero usar exceções para controle de fluxo! A falha às vezes é esperado.

Foi útil?

Solução

Pessoalmente, acho que eu usaria a mesma idéia que TryParse (): usando um parâmetro de saída para a saída do valor real, e retornando um booleano que indica se a chamada foi bem sucedida ou não

public bool CalculatePoint(... out Point result);

Eu não sou um fã do uso de exceção para comportamentos "normais" (se você espera que a função não funcionar para algumas entradas).

Outras dicas

Por que eles falham? Se é por causa de algo que o chamador tem feito (ou seja, os argumentos apresentados), em seguida, jogando ArgumentException é inteiramente apropriado. Um método Tente [...] que evita a exceção é bom.

Eu acho que é uma boa idéia para fornecer a versão que lança uma exceção, porém, para que os chamadores que esperam que eles sempre fornecem bons dados receberá uma mensagem adequadamente forte (isto é uma exceção) se eles estão sempre errados.

Outra alternativa é lançar uma exceção. No entanto, você geralmente só querem lançar exceções em "casos excepcionais".

Se os casos de falha são comuns (e não excepcional), então você já listados para fora suas duas opções. Editar : Pode haver uma convenção em seu projeto como a forma de lidar com casos tais não-excepcionais (se se deve retornar sucesso ou o objeto). Se não houver convenção existente, então eu concordo com lucasbfr e sugerir você retornar sucesso (que concorda com a forma como TryParse (...) é projetado).

Se a falha for por uma razão específica, então eu acho que é ok como nulo retorno, ou bool e ter um parâmetro de saída. Se, contudo, você retornar nulo, independentemente da falha, então eu não recomendo. Exceções fornecem um rico conjunto de informações, incluindo a razão pela qual algo falhou, se tudo que você voltar é um nulo, em seguida, como você sabe se o seu porque os dados está errado, você ficou sem memória ou algum outro comportamento estranho.

Mesmo em .net o TryParse tem um irmão Parse de modo que você pode obter a exceção se você quiser.

Se eu fornecido um método TrySomething Também gostaria de fornecer um método Algo que emitiu uma exceção em caso de fracasso. Então cabe ao autor da chamada.

O modelo que eu usei é a mesma MS usos com os métodos TryParse de várias classes.

O seu código original:
pública Ponto CalculatePoint (... fora booleana boSuccess);
Lista pública CalculateListOfPoints (... fora booleana boSuccess);

iria se transformar em CalculatePoint bool público (... fora (ou ref) Ponto CalculatedValue);
CalculateListOfPoints bool pública (... fora (ou ref) CalculatedValues ??Lista);

Basicamente você faz o sucesso / fracasso o valor de retorno.

Para resumir, há um par de abordagens que você pode tomar:

  1. Quando o tipo de retorno é um tipo de valor, como Point, utilize o recurso Nullable de C # e retornar um ponto? (Aka Nullable), de que maneira você ainda pode retornar nulo em uma falha
  2. lançar uma exceção quando há uma falha. O todo argumento / discussão sobre o que é e não é "excepcional" é um ponto discutível, é a sua API, você decide o que é um comportamento excepcional.
  3. Adote um modelo semelhante ao aplicado pela Microsoft nos tipos base como Int32, fornecer uma CalculatePoint e TryCalculatePoint (Int32.Parse e int32.TryParse) e tem um lance e um retorno um bool.
  4. Voltar uma estrutura genérica de seus métodos que tem duas propriedades, bool sucesso e GenericType Valor.

Dependendo do cenário que tendem a usar uma combinação de retornar nulo ou lançar uma exceção como parecem "mais limpa" para mim e se encaixam melhor com a base de código existente na empresa em que trabalho. Assim, a minha melhor prática pessoal seria se aproxima de 1 e 2.

Depende principalmente o comportamento de seus métodos e seu uso.

Se a falha é comum e não-crítico, então têm seus métodos retornam um booleano que indica seu sucesso e usar um parâmetro de saída para transmitir o resultado. Olhando-se uma chave em um hash, a tentativa de ler dados em um soquete sem bloqueio quando não há dados disponíveis, todos estes exemplos caem nessa categoria.

Se a falha é inesperado, retornar diretamente o resultado e transmitir erros com exceções. Abrir um arquivo somente leitura, conectando a um servidor TCP, são bons candidatos.

E, às vezes ambas as maneiras fazer sentido ...

Voltar Point.Empty . É um patter projeto .NET para retornar um campo especial quando você quer verificar se a criação estrutura foi bem sucedida. Evite a parâmetros quando puder.

public static readonly Point Empty

Um teste padrão que eu estou experimentando com está retornando um Talvez . Ele tem a semântica da TryParse padrão, mas uma assinatura semelhante ao padrão nulo retorno sobre o erro.

Eu não estou ainda convencido uma forma ou de outra, mas eu oferecê-lo para sua consideração coletiva. Ele tem a vantagem de não necessitar de uma variável para definido antes da chamada de método para manter o parâmetro de saída no site da chamada do método. Também poderia ser estendido com um Erros ou Mensagens coleção para indicar o motivo da falha.

A aparência Talvez classe algo como isto:

/// <summary>
/// Represents the return value from an operation that might fail
/// </summary>
/// <typeparam name="T"></typeparam>
public struct Maybe<T>
{
    T _value;
    bool _hasValue;


    public Maybe(T value)
    {
        _value = value;
        _hasValue = true;
    }

    public Maybe()
    {
        _hasValue = false;
        _value = default(T);
    }


    public bool Success
    {
        get { return _hasValue; }
    }


    public T Value
    {
        get 
            { // could throw an exception if _hasValue is false
              return _value; 
            }
    }
}

Eu diria que a melhor prática é um meio valor de retorno sucesso, e um meios exceção fracasso.

Não vejo razão nos exemplos que você, desde que você não deve estar usando exceções em caso de uma falha.

Usando uma exceção é uma má idéia, em alguns casos (especialmente quando se escreve um servidor). Você precisaria de dois sabores do método. Também olhar para a classe de dicionário para uma indicação de que você deve fazer.

// NB:  A bool is the return value. 
//      This makes it possible to put this beast in if statements.
public bool TryCalculatePoint(... out Point result) { }

public Point CalculatePoint(...)
{
   Point result;
   if(!TryCalculatePoint(... out result))
       throw new BogusPointException();
   return result;
}

O melhor de dois mundos!

O bool TrySomething () é, pelo menos, uma prática, que funciona ok para o .net métodos de análise, mas eu não acho que eu gosto em geral.

lançar uma exceção é muitas vezes uma coisa boa, embora ele não deve ser utilizado para situações que você espera que aconteça em muitas situações normais, e tem um custo de desempenho associados.

Voltando nulo quando possível é na maioria dos casos ok, quando você não quer uma exceção.

No entanto - a sua abordagem é um pouco processual - o que sobre a criação de algo como uma classe PointCalculator - tomando os dados necessários como parâmetros no construtor? Então você chama CalculatePoint sobre ele, e acesso o resultado através de propriedades (propriedades separadas para Point e para o Sucesso).

Você não quer estar jogando exceções quando há algo esperado acontecendo, como @ Kevin afirmou exceções são para casos excepcionais.

Você deve retornar algo que é esperado para o 'fracasso', geralmente nula é a minha escolha de retorno ruim.

A documentação para o seu método deve informar os utilizadores sobre o que esperar quando os dados Não computa .

Nós escreveu uma vez um quadro inteiro onde todos os métodos públicos devolvidos verdadeiro (executada com sucesso) ou falso (ocorreu um erro). Se precisávamos para retornar um valor que usamos parâmetros de saída. Ao contrário da crença popular, este modo de programação realmente simplificado um monte de nosso código.

Bem com Point, você pode enviar de volta Point.Empty como um valor de retorno em caso de falha. Ora, tudo isso realmente faz é retornar um ponto com 0 para o valor X e Y, então se isso pode ser um valor de retorno válido, eu ficaria longe disso, mas se o seu método nunca retornará um (0,0) ponto , então você pode usar isso.

Desculpe, eu só lembrei do tipo anulável, você deve olhar para isso. Eu não estou muito certo o que a sobrecarga é apesar de tudo.

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