Domanda

Questa domanda è un seguito di Come indicare che un metodo fallito . Il modello xxx () Tryxxx () è qualcosa che può essere molto utile in molte librerie. Mi chiedo quale sia il modo migliore per offrire entrambe le implementazioni senza duplicare il mio codice.

Cosa è meglio:

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

Suppongo istintivamente che il primo esempio sia più corretto (sai esattamente quale eccezione si è verificata), ma il tentativo / cattura non potrebbe essere troppo costoso? C'è un modo per cogliere l'eccezione nel secondo esempio?

È stato utile?

Soluzione

Fare una prova Qualcosa che cattura e ingoia l'eccezione è una pessima idea. La metà del punto del modello TryXXX è quella di evitare il successo delle eccezioni.

Se non hai bisogno di molte informazioni nell'eccezione, puoi fare in modo che il metodo DoSomething chiami TrySomething e genera un'eccezione se fallisce. Se hai bisogno di dettagli nell'eccezione, potresti aver bisogno di qualcosa di più elaborato. Non ho cronometrato dove si trova la maggior parte della performance hit delle eccezioni: se si tratta del lancio anziché della creazione, è possibile scrivere un metodo privato con una firma simile a TrySomething, ma che ha restituito un'eccezione o null:

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 questo prima di impegnarti comunque!

Altri suggerimenti

Di solito uso questo modello. Dipende da come viene implementato il metodo interno per capire se questo abbia o meno senso. Se devi usare i blocchi catch condizionali può diventare un po 'sgradevole ...

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

Il primo esempio è corretto se stai per cogliere l'eccezione e non fai nulla ma restituisci false con essa.

Potresti cambiare TrySomething per assomigliare di seguito.

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

Quindi DoSomething sarebbe simile

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 non volevi TrySomething con throwException esposto al pubblico, puoi renderlo un membro privato.

Le eccezioni potrebbero diventare costose e potresti fare qualche controllo RegEx sulla stringa per evitare che ne venga lanciata una. Dipende da cosa stai cercando di fare.

Supponendo che questo sia C #, direi il secondo esempio

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

Imita il int.TryParse incorporato (stringhe, risultato int int) , e secondo me è meglio rimanere coerenti con la lingua / l'ambiente.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top