Pregunta

Esta pregunta es un seguimiento de Cómo indicar que un El método no tuvo éxito . El patrón xxx () Tryxxx () es algo que puede ser muy útil en muchas bibliotecas. Me pregunto cuál es la mejor manera de ofrecer ambas implementaciones sin duplicar mi código.

Lo que es mejor:

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;
    }

o

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)
{
    //...
}

Supondría instintivamente que el primer ejemplo es más correcto (ya sabes exactamente qué excepción ocurrió), pero ¿no podría el intentar / atrapar ser demasiado caro? ¿Hay alguna manera de detectar la excepción en el segundo ejemplo?

¿Fue útil?

Solución

Hacer TryTomething simplemente atrapar y tragar la excepción es una muy mala idea. La mitad del punto del patrón TryXXX es evitar el golpe de rendimiento de las excepciones.

Si no necesita mucha información en la excepción, podría hacer que el método DoSomething simplemente llame a TrySomething y lance una excepción si falla. Si necesita detalles en la excepción, es posible que necesite algo más detallado. No he cronometrado donde está la mayor parte del impacto en el rendimiento de las excepciones: si se trata de lanzar en lugar de crear, podría escribir un método privado que tuviera una firma similar a TrySomething, pero que devolviera una excepción o un valor 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");
    }
}

Sin embargo, ¡tómalo antes de que te comprometas!

Otros consejos

Normalmente uso este patrón. Depende de cómo se implemente el método interno en cuanto a si esto tiene algún sentido o no. Si tiene que usar bloques de captura condicionales, puede ser un poco desagradable ...

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 */
}

El primer ejemplo es correcto si solo va a detectar la excepción y no hace nada, pero devuelva false con ella.

Puede cambiar TrySomething para que se vea a continuación.

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);
}

Entonces, DoSomething se vería como

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;      
}

Si no desea que TrySomething con throwException se exponga al público, puede hacerlo miembro privado.

Las excepciones pueden ser costosas y usted podría hacer algunas verificaciones RegEx en la cadena para evitar que se lance una. Depende de lo que intentes hacer.

Suponiendo que esto es C #, diría que el segundo ejemplo

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

Imita el int.TryParse integrado (string s, out int int result) , y en mi opinión, es mejor permanecer coherente con el idioma / entorno.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top