Pergunta

Esta questão é um follow-up de Como indicam que um método foi vencida. O padrão de xxx () Tryxxx () é algo que pode ser muito útil em muitas bibliotecas. Eu estou querendo saber qual é a melhor maneira de oferecer ambas as implementações sem duplicar o meu código.

O que é melhor:

public int DoSomething(string a)
{
     // might throw an exception
}
public bool TrySomething(string a, out result)
{
    try
    {
        result = DoSomething(a)
        return true;
    }
    catch (Exception)
    {
        return false;
    }

ou

public int DoSomething(string a)
{
     int result;
     if (TrySomething(a, out result))
     {
         return result;
     }
     else
     {
         throw Exception(); // which exception?
     }
}
public bool TrySomething(string a, out result)
{
    //...
}

Eu instintivamente assumir que o primeiro exemplo é mais correto (você sabe exatamente qual exceção aconteceu), mas não conseguiu o try / catch ser muito caro? Existe uma maneira de capturar a exceção no segundo exemplo?

Foi útil?

Solução

Fazendo TrySomething apenas pegar e engolir a exceção é uma péssima idéia. Metade do ponto do padrão TryXXX é evitar a batida de exceções desempenho.

Se você não precisa de muita informação na exceção, você poderia fazer o método DoSomething apenas chamar TrySomething e lançar uma exceção se ele falhar. Se você precisar de detalhes na exceção, você pode precisar de algo mais elaborado. Eu não ter atingido o tempo em que a maior parte do sucesso de exceções desempenho é - se é o lançamento, em vez da criação, você poderia escrever um método particular que tinha uma assinatura semelhante ao TrySomething, mas que retornou uma exceção ou nulo:

public int DoSomething(string input)
{
    int ret;
    Exception exception = DoSomethingImpl(input, out ret);
    if (exception != null)
    {
        // Note that you'll lose stack trace accuracy here
        throw exception;
    }
    return ret;
}

public bool TrySomething(string input, out int ret)
{
    Exception exception = DoSomethingImpl(input, out ret);
    return exception == null;
}

private Exception DoSomethingImpl(string input, out int ret)
{
    ret = 0;
    if (input != "bad")
    {
        ret = 5;
        return null;
    }
    else
    {
        return new ArgumentException("Some details");
    }
}

Tempo isso antes de se comprometer com ele embora!

Outras dicas

Eu costumo usar esse padrão. Depende de como o método interno é implementado como se ou não isso faz qualquer sentido. Se você tem que usar blocos catch condicionais ele pode ficar um pouco desagradável ...

public object DoSomething(object input){
  return DoSomethingInternal(input, true);
}

public bool TryDoSomething(object input, out object result){
  result = DoSomethingInternal(input, false);
  return result != null;
}

private object DoSomethingInternal(object input, bool throwOnError){
  /* do your work here; only throw if you cannot proceed and throwOnError is true */
}

O primeiro exemplo é correto se você está indo só para capturar a exceção e não fazer nada, mas falsa retorno com ele.

Você pode mudar TrySomething para olhar como abaixo.

public bool TrySomething(string a, out result, bool throwException)
{
  try
  {
    // Whatever
  }
  catch
  {
    if(throwException)
    {
      throw;
    }
    else
    {
      return false;
    }
  }

}

public bool TrySomething(string a, out result)
{
  return TrySomething(a, out result, false);
}

Assim DoSomething seria parecido

public int DoSomething(string a)
{
  int result;

  // This will throw the execption or 
  // change to false to not, or don't use the overloaded one.
  TrySomething(a, out result, true) 

  return result;      
}

Se você não queria TrySomething com throwException exposta ao público, você pode torná-lo um membro privado.

Exceções poderia ficar caro e você poderia fazer alguma RegEx verificando na corda para impedir alguém de ser jogado. Depende do que você está tentando fazer.

Assumindo que este é C #, eu diria que o segundo exemplo

public bool TrySomething(string a, out result)
{
    try
    {
        result = DoSomething(a)
        return true;
    }
    catch (Exception)
    {
        return false;
    }
}

Ele imita o construído em int.TryParse(string s, out int result), e na minha opinião o seu melhor para ficar consistente com a linguagem / ambiente.

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