Pattern per evitare annidati provare a catturare i blocchi?


114

Considera una situazione in cui ho tre (o più) modi per eseguire un calcolo, ognuno dei quali può fallire con un'eccezione. Per tentare ogni calcolo finché non ne troviamo uno che abbia esito positivo, ho fatto quanto segue:

double val;

try { val = calc1(); }
catch (Calc1Exception e1)
{ 
    try { val = calc2(); }
    catch (Calc2Exception e2)
    {
        try { val = calc3(); }
        catch (Calc3Exception e3)
        {
            throw new NoCalcsWorkedException();
        }
    }
}

Esiste un modello accettato che consenta di raggiungere questo obiettivo in modo migliore? Ovviamente potrei racchiudere ogni calcolo in un metodo di supporto che restituisce null in caso di errore e quindi utilizzare semplicemente l' ??operatore, ma esiste un modo per farlo più in generale (ovvero senza dover scrivere un metodo di supporto per ogni metodo che voglio utilizzare )? Ho pensato di scrivere un metodo statico utilizzando generics che racchiuda un determinato metodo in un try / catch e restituisca null in caso di errore, ma non sono sicuro di come procederei. Qualche idea?


Potete includere alcuni dettagli sul calcolo?
James Johnson

2
Sono fondamentalmente solo metodi diversi per risolvere / approssimare una PDE. Provengono da una libreria di terze parti, quindi non posso modificarli per restituire codici di errore o null. Il meglio che potrei fare è avvolgere ciascuno individualmente in un metodo.
jjoelson

I metodi calc fanno parte del tuo progetto (invece di una libreria di terze parti)? In tal caso, è possibile estrarre la logica che genera eccezioni e utilizzarla per decidere quale metodo calc deve essere chiamato.
Chris

1
C'è un altro caso d'uso per questo che ho riscontrato in Java: ho bisogno di analizzare un Stringin un Dateutilizzo SimpleDateFormat.parsee devo provare diversi formati in ordine, passando a quello successivo quando si genera un'eccezione.
Miserabile variabile

Risposte:


125

Per quanto possibile, non utilizzare eccezioni per il flusso di controllo o circostanze non eccezionali.

Ma per rispondere direttamente alla tua domanda (supponendo che tutti i tipi di eccezione siano gli stessi):

Func<double>[] calcs = { calc1, calc2, calc3 };

foreach(var calc in calcs)
{
   try { return calc(); }
   catch (CalcException){  }
} 

throw new NoCalcsWorkedException();

15
Ciò presuppone che Calc1Exception, Calc2Exceptione Calc3Exceptioncondividano una classe base comune.
Wyzard

3
In cima, assume una firma comune, che non è poi così lontana. Buona risposta.
TomTom

1
Inoltre, ho aggiunto continuenel blocco catch e breakdopo il blocco catch in modo che il ciclo termini quando un calcolo funziona (grazie a Lirik per questo bit)
jjoelson

6
+1 solo perché dice "Non usare eccezioni per il flusso di controllo" anche se avrei usato "MAI E MAI" invece di "Per quanto possibile".
Bill K

1
@jjoelson: una breakdichiarazione che segue calc();all'interno del try(e nessuna continuedichiarazione) potrebbe essere un po 'più idiomatica.
Adam Robinson

38

Solo per offrire un'alternativa "fuori dagli schemi", che ne dici di una funzione ricorsiva ...

//Calling Code
double result = DoCalc();

double DoCalc(int c = 1)
{
   try{
      switch(c){
         case 1: return Calc1();
         case 2: return Calc2();
         case 3: return Calc3();
         default: return CalcDefault();  //default should not be one of the Calcs - infinite loop
      }
   }
   catch{
      return DoCalc(++c);
   }
}

NOTA: non sto affatto dicendo che questo sia il modo migliore per portare a termine il lavoro, solo un modo diverso


6
Ho dovuto implementare "On Error Resume Next" in una lingua una volta e il codice che ho generato assomigliava molto a questo.
Jacob Krall

4
Per favore, non usare mai un'istruzione switch per creare un ciclo for.
Jeff Ferland

3
Non è possibile mantenere un'istruzione switch per il looping
Mohamed Abed

1
So che la mia risposta non è il codice più efficiente, ma poi di nuovo usare i blocchi try / catch per questo genere di cose non è il modo migliore per andare comunque. Sfortunatamente, l'OP utilizza una libreria di terze parti e deve fare del suo meglio per garantire il successo. Idealmente, l'input potrebbe essere prima convalidato e la funzione di calcolo corretta scelta per garantire che non fallisca - ovviamente, potresti quindi mettere tutto ciò in una prova / cattura solo per sicurezza;)
musefan

1
return DoCalc(c++)è equivalente a return DoCalc(c): il valore post-incrementato non verrà passato più in profondità. Per farlo funzionare (e introdurre più oscurità) potrebbe essere più simile return DoCalc((c++,c)).
Artur Czajka

37

Puoi appiattire l'annidamento inserendolo in un metodo come questo:

private double calcStuff()
{
  try { return calc1(); }
  catch (Calc1Exception e1)
  {
    // Continue on to the code below
  }

  try { return calc2(); }
  catch (Calc2Exception e1)
  {
    // Continue on to the code below
  }

  try { return calc3(); }
  catch (Calc3Exception e1)
  {
    // Continue on to the code below
  }

  throw new NoCalcsWorkedException();
}

Ma sospetto che il file vero problema di progettazione sia l'esistenza di tre metodi diversi che fanno essenzialmente la stessa cosa (dal punto di vista del chiamante) ma generano eccezioni diverse e non correlate.

Ciò presuppone che le tre eccezioni non siano correlate. Se hanno tutti una classe base comune, sarebbe meglio usare un ciclo con un unico blocco catch, come suggerito da Ani.


1
+1: Questa è la soluzione più pulita e concreta al problema. Le altre soluzioni che vedo qui cercano solo di essere carine, IMO. Come ha detto l'OP, non ha scritto l'API, quindi è bloccato con le eccezioni che vengono lanciate.
Nate CK

19

Cerca di non controllare la logica in base alle eccezioni; nota anche che le eccezioni dovrebbero essere lanciate solo in casi eccezionali. I calcoli nella maggior parte dei casi non dovrebbero generare eccezioni a meno che non accedano a risorse esterne o analizzino stringhe o qualcosa del genere. Ad ogni modo nel peggiore dei casi segui lo stile TryMethod (come TryParse ()) per incapsulare la logica delle eccezioni e rendere il tuo flusso di controllo mantenibile e pulito:

bool TryCalculate(out double paramOut)
{
  try
  {
    // do some calculations
    return true;
  }
  catch(Exception e)
  { 
     // do some handling
    return false;
  }

}

double calcOutput;
if(!TryCalc1(inputParam, out calcOutput))
  TryCalc2(inputParam, out calcOutput);

Un'altra variante che utilizza il modello Try e combina l'elenco di metodi invece di annidato se:

internal delegate bool TryCalculation(out double output);

TryCalculation[] tryCalcs = { calc1, calc2, calc3 };

double calcOutput;
foreach (var tryCalc in tryCalcs.Where(tryCalc => tryCalc(out calcOutput)))
  break;

e se il foreach è un po 'complicato puoi renderlo chiaro:

        foreach (var tryCalc in tryCalcs)
        {
            if (tryCalc(out calcOutput)) break;
        }

Onestamente, penso che questo causi solo astrazioni non necessarie. Questa non è una soluzione orribile, ma non la userei per la maggior parte dei casi.
user606723

Se non ti interessa il tipo di eccezione e vuoi solo gestire il codice condizionale .. quindi è decisamente meglio in termini di astrazione e manutenibilità convertirlo in un metodo condizionale con ritorno, indipendentemente dal fatto che sia riuscito o meno, in questo modo nascondi l'eccezione che gestisce la sintassi disordinata con un chiaro metodo descrittivo .. quindi il tuo codice lo gestirà in quanto è un normale metodo condizionale.
Mohamed Abed

Conosco i punti e sono validi. Tuttavia, quando si utilizza questo tipo di astrazione (nascondendo disordine / complessità) quasi ovunque diventa ridicolo e capire un pezzo di software per quello che è diventa molto più difficile. Come ho detto, non è una soluzione terribile, ma non la userei alla leggera.
user606723

9

Crea un elenco di delegati alle tue funzioni di calcolo e poi fai un ciclo while per scorrerli:

List<Func<double>> calcMethods = new List<Func<double>>();

// Note: I haven't done this in a while, so I'm not sure if
// this is the correct syntax for Func delegates, but it should
// give you an idea of how to do this.
calcMethods.Add(new Func<double>(calc1));
calcMethods.Add(new Func<double>(calc2));
calcMethods.Add(new Func<double>(calc3));

double val;
for(CalcMethod calc in calcMethods)
{
    try
    {
        val = calc();
        // If you didn't catch an exception, then break out of the loop
        break;
    }
    catch(GenericCalcException e)
    {
        // Not sure what your exception would be, but catch it and continue
    }

}

return val; // are you returning the value?

Questo dovrebbe darti un'idea generale di come farlo (cioè non è una soluzione esatta).


1
Tranne ovviamente il fatto che normalmente non dovresti mai prenderlo Exception. ;)
DeCaf

@DeCaf come ho detto: che "dovrebbe darti un'idea generale di come farlo (cioè non una soluzione esatta)." Quindi l'OP può catturare qualunque sia l'eccezione appropriata ... non c'è bisogno di catturare il generico Exception.
Kiril

Sì, scusa, ho solo sentito il bisogno di farlo uscire.
DeCaf

1
@DeCaf, è un valido chiarimento per chi magari non conosce bene le best practice. Grazie :)
Kiril

9

Sembra un lavoro per ... MONADS! Nello specifico, la monade Forse. Inizia con la monade Forse come descritto qui . Quindi aggiungi alcuni metodi di estensione. Ho scritto questi metodi di estensione specificamente per il problema come lo hai descritto. La cosa bella delle monadi è che puoi scrivere i metodi di estensione esatti necessari per la tua situazione.

public static Maybe<T> TryGet<T>(this Maybe<T> m, Func<T> getFunction)
{
    // If m has a value, just return m - we want to return the value
    // of the *first* successful TryGet.
    if (m.HasValue)
    {
        return m;
    }

    try
    {
        var value = getFunction();

        // We were able to successfully get a value. Wrap it in a Maybe
        // so that we can continue to chain.
        return value.ToMaybe();
    }
    catch
    {
        // We were unable to get a value. There's nothing else we can do.
        // Hopefully, another TryGet or ThrowIfNone will handle the None.
        return Maybe<T>.None;
    }
}

public static Maybe<T> ThrowIfNone<T>(
    this Maybe<T> m,
    Func<Exception> throwFunction)
{
    if (!m.HasValue)
    {
        // If m does not have a value by now, give up and throw.
        throw throwFunction();
    }

    // Otherwise, pass it on - someone else should unwrap the Maybe and
    // use its value.
    return m;
}

Usalo così:

[Test]
public void ThrowIfNone_ThrowsTheSpecifiedException_GivenNoSuccessfulTryGet()
{
    Assert.That(() =>
        Maybe<double>.None
            .TryGet(() => { throw new Exception(); })
            .TryGet(() => { throw new Exception(); })
            .TryGet(() => { throw new Exception(); })
            .ThrowIfNone(() => new NoCalcsWorkedException())
            .Value,
        Throws.TypeOf<NoCalcsWorkedException>());
}

[Test]
public void Value_ReturnsTheValueOfTheFirstSuccessfulTryGet()
{
    Assert.That(
        Maybe<double>.None
            .TryGet(() => { throw new Exception(); })
            .TryGet(() => 0)
            .TryGet(() => 1)
            .ThrowIfNone(() => new NoCalcsWorkedException())
            .Value,
        Is.EqualTo(0));
}

Se ti ritrovi a fare spesso questo tipo di calcoli, forse la monade dovrebbe ridurre la quantità di codice standard che devi scrivere aumentando la leggibilità del tuo codice.


2
Amo questa soluzione. Tuttavia, è abbastanza opaco per chiunque non abbia avuto precedentemente esposizione alle monadi, il che significa che questo è molto lontano dall'essere idiomatico in c #. Non vorrei che uno dei miei colleghi imparasse le monadi solo per modificare questo stupido pezzo di codice in futuro. Questo è ottimo per riferimento futuro, però.
jjoelson

1
+1 per il senso dell'umorismo, per aver scritto la soluzione più ottusa e prolissa possibile a questo problema e poi dicendo che "ridurrà la quantità di codice standard che devi scrivere aumentando la leggibilità del tuo codice".
Nate CK

1
Ehi, non ci lamentiamo delle enormi quantità di codice in agguato in System.Linq e usiamo felicemente quelle monadi tutto il giorno. Penso che @ fre0n significhi semplicemente che se sei disposto a mettere la monade Forse nel tuo toolkit, questo tipo di valutazioni concatenate diventa più facile da guardare e ragionare. Esistono diverse implementazioni facili da acquisire.
Sebastian Good

Solo perché usa Maybenon rende questa una soluzione monadica; usa zero delle proprietà monadiche di Maybee quindi può anche usare null. Inoltre, usato "monadicamente" questo sarebbe l' inverso di Maybe. Una vera soluzione monadica dovrebbe utilizzare una monade di stato che mantiene il primo valore non eccezionale come suo stato, ma sarebbe eccessivo quando la normale valutazione concatenata funziona.
Dax Fohl

7

Un'altra versione dell'approccio del metodo try . Questo consente eccezioni tipizzate, poiché esiste un tipo di eccezione per ogni calcolo:

    public bool Try<T>(Func<double> func, out double d) where T : Exception
    {
      try
      {
        d = func();
        return true;
      }
      catch (T)
      {
        d = 0;
        return false;
      }
    }

    // usage:
    double d;
    if (!Try<Calc1Exception>(() = calc1(), out d) && 
        !Try<Calc2Exception>(() = calc2(), out d) && 
        !Try<Calc3Exception>(() = calc3(), out d))

      throw new NoCalcsWorkedException();
    }

Potresti effettivamente evitare i se annidati usando invece &&tra ogni condizione.
DeCaf

4

In Perl puoi farlo foo() or bar(), che verrà eseguito bar()se foo()fallisce. In C # non vediamo questo costrutto "if fail, then", ma c'è un operatore che possiamo usare per questo scopo: l'operatore null-coalesce ??, che continua solo se la prima parte è nulla.

Se puoi modificare la firma dei tuoi calcoli e se racchiudi le loro eccezioni (come mostrato nei post precedenti) o le riscrivi per restituirle null, la tua catena di codici diventa sempre più breve e ancora di facile lettura:

double? val = Calc1() ?? Calc2() ?? Calc3() ?? Calc4();
if(!val.HasValue) 
    throw new NoCalcsWorkedException();

Ho usato le seguenti sostituzioni per le tue funzioni, il che si traduce nel valore 40.40in val.

static double? Calc1() { return null; /* failed */}
static double? Calc2() { return null; /* failed */}
static double? Calc3() { return null; /* failed */}
static double? Calc4() { return 40.40; /* success! */}

Mi rendo conto che questa soluzione non sarà sempre applicabile, ma hai posto una domanda molto interessante e credo, anche se il filo è relativamente vecchio, che questo sia uno schema che vale la pena considerare quando puoi fare ammenda.


1
Voglio solo dire "Grazie". Ho provato a mettere in pratica ciò di cui stavi parlando . Spero di aver capito bene.
AlexMelw

3

Dato che i metodi di calcolo hanno la stessa firma senza parametri, è possibile registrarli in un elenco, scorrere tale elenco ed eseguire i metodi. Probabilmente sarebbe ancora meglio per te usare il Func<double>significato "una funzione che restituisce un risultato di tipo double".

using System;
using System.Collections.Generic;

namespace ConsoleApplication1
{
  class CalculationException : Exception { }
  class Program
  {
    static double Calc1() { throw new CalculationException(); }
    static double Calc2() { throw new CalculationException(); }
    static double Calc3() { return 42.0; }

    static void Main(string[] args)
    {
      var methods = new List<Func<double>> {
        new Func<double>(Calc1),
        new Func<double>(Calc2),
        new Func<double>(Calc3)
    };

    double? result = null;
    foreach (var method in methods)
    {
      try {
        result = method();
        break;
      }
      catch (CalculationException ex) {
        // handle exception
      }
     }
     Console.WriteLine(result.Value);
   }
}

3

È possibile utilizzare Task / ContinueWith e verificare l'eccezione. Ecco un bel metodo di estensione per renderlo carino:

    static void Main() {
        var task = Task<double>.Factory.StartNew(Calc1)
            .OrIfException(Calc2)
            .OrIfException(Calc3)
            .OrIfException(Calc4);
        Console.WriteLine(task.Result); // shows "3" (the first one that passed)
    }

    static double Calc1() {
        throw new InvalidOperationException();
    }

    static double Calc2() {
        throw new InvalidOperationException();
    }

    static double Calc3() {
        return 3;
    }

    static double Calc4() {
        return 4;
    }
}

static class A {
    public static Task<T> OrIfException<T>(this Task<T> task, Func<T> nextOption) {
        return task.ContinueWith(t => t.Exception == null ? t.Result : nextOption(), TaskContinuationOptions.ExecuteSynchronously);
    }
}

1

Se il tipo effettivo dell'eccezione generata non ha importanza, puoi semplicemente utilizzare un blocco catch senza tipo:

var setters = new[] { calc1, calc2, calc3 };
bool succeeded = false;
foreach(var s in setters)
{
    try
    {
            val = s();
            succeeded = true;
            break;
    }
    catch { /* continue */ }
}
if (!suceeded) throw new NoCalcsWorkedException();

Non chiama sempre ogni funzione nell'elenco? Potrebbe voler lanciare (gioco di parole non inteso) in qualcosa come if(succeeded) { break; }post-cattura.
un CVn

1
using System;

namespace Utility
{
    /// <summary>
    /// A helper class for try-catch-related functionality
    /// </summary>
    public static class TryHelper
    {
        /// <summary>
        /// Runs each function in sequence until one throws no exceptions;
        /// if every provided function fails, the exception thrown by
        /// the final one is left unhandled
        /// </summary>
        public static void TryUntilSuccessful( params Action[] functions )
        {
            Exception exception = null;

            foreach( Action function in functions )
            {
                try
                {
                    function();
                    return;
                }
                catch( Exception e )
                {
                    exception   = e;
                }
            }

            throw exception;
        }
    }
}

E usalo in questo modo:

using Utility;

...

TryHelper.TryUntilSuccessful(
    () =>
    {
        /* some code */
    },
    () =>
    {
        /* more code */
    },
    calc1,
    calc2,
    calc3,
    () =>
    {
        throw NotImplementedException();
    },
    ...
);

1

Sembra che l'intenzione dell'OP fosse quella di trovare un buon modello per risolvere il suo problema e risolvere il problema attuale con cui stava lottando in quel momento.

OP: "Potrei racchiudere ogni calcolo in un metodo di supporto che restituisce null in caso di errore, e quindi utilizzare semplicemente l' ??operatore, ma esiste un modo per farlo più in generale (cioè senza dover scrivere un metodo di supporto per ogni metodo che desidero use)? Ho pensato di scrivere un metodo statico utilizzando generics che racchiude un dato metodo in un try / catch e restituisce null in caso di fallimento, ma non sono sicuro di come potrei procedere. Qualche idea? "

Ho visto molti buoni modelli che evitano i blocchi try catch annidati , pubblicati in questo feed, ma non ho trovato una soluzione al problema sopra citato. Quindi, ecco la soluzione:

Come OP menzionato sopra, voleva creare un oggetto wrapper che ritorni nullin caso di fallimento . Lo chiamerei pod ( pod a prova di eccezioni ).

public static void Run()
{
    // The general case
    // var safePod1 = SafePod.CreateForValueTypeResult(() => CalcX(5, "abc", obj));
    // var safePod2 = SafePod.CreateForValueTypeResult(() => CalcY("abc", obj));
    // var safePod3 = SafePod.CreateForValueTypeResult(() => CalcZ());

    // If you have parameterless functions/methods, you could simplify it to:
    var safePod1 = SafePod.CreateForValueTypeResult(Calc1);
    var safePod2 = SafePod.CreateForValueTypeResult(Calc2);
    var safePod3 = SafePod.CreateForValueTypeResult(Calc3);

    var w = safePod1() ??
            safePod2() ??
            safePod3() ??
            throw new NoCalcsWorkedException(); // I've tested it on C# 7.2

    Console.Out.WriteLine($"result = {w}"); // w = 2.000001
}

private static double Calc1() => throw new Exception("Intentionally thrown exception");
private static double Calc2() => 2.000001;
private static double Calc3() => 3.000001;

Ma cosa succede se si desidera creare un pod sicuro per un risultato del tipo di riferimento restituito dalle funzioni / metodi CalcN ().

public static void Run()
{
    var safePod1 = SafePod.CreateForReferenceTypeResult(Calc1);
    var safePod2 = SafePod.CreateForReferenceTypeResult(Calc2);
    var safePod3 = SafePod.CreateForReferenceTypeResult(Calc3);

    User w = safePod1() ?? safePod2() ?? safePod3();

    if (w == null) throw new NoCalcsWorkedException();

    Console.Out.WriteLine($"The user object is {{{w}}}"); // The user object is {Name: Mike}
}

private static User Calc1() => throw new Exception("Intentionally thrown exception");
private static User Calc2() => new User { Name = "Mike" };
private static User Calc3() => new User { Name = "Alex" };

class User
{
    public string Name { get; set; }
    public override string ToString() => $"{nameof(Name)}: {Name}";
}

Quindi, potresti notare che non è necessario "scrivere un metodo di supporto per ogni metodo che desideri utilizzare" .

I due tipi di cialde (per ValueTypeResults e ReferenceTypeResults) sono sufficienti .


Ecco il codice di SafePod. Tuttavia non è un contenitore. Invece, crea un wrapper delegato sicuro dalle eccezioni sia per ValueTypeResults che per ReferenceTypeResults.

public static class SafePod
{
    public static Func<TResult?> CreateForValueTypeResult<TResult>(Func<TResult> jobUnit) where TResult : struct
    {
        Func<TResult?> wrapperFunc = () =>
        {
            try { return jobUnit.Invoke(); } catch { return null; }
        };

        return wrapperFunc;
    }

    public static Func<TResult> CreateForReferenceTypeResult<TResult>(Func<TResult> jobUnit) where TResult : class
    {
        Func<TResult> wrapperFunc = () =>
        {
            try { return jobUnit.Invoke(); } catch { return null; }
        };

        return wrapperFunc;
    }
}

È così che puoi sfruttare l'operatore di coalescenza nullo ??combinato con la potenza di entità cittadine di prima classedelegate .


0

Hai ragione riguardo al confezionamento di ogni calcolo, ma dovresti avvolgere secondo il principio del tell-don't-ask.

double calc3WithConvertedException(){
    try { val = calc3(); }
    catch (Calc3Exception e3)
    {
        throw new NoCalcsWorkedException();
    }
}

double calc2DefaultingToCalc3WithConvertedException(){
    try { val = calc2(); }
    catch (Calc2Exception e2)
    {
        //defaulting to simpler method
        return calc3WithConvertedException();
    }
}


double calc1DefaultingToCalc2(){
    try { val = calc2(); }
    catch (Calc1Exception e1)
    {
        //defaulting to simpler method
        return calc2defaultingToCalc3WithConvertedException();
    }
}

Le operazioni sono semplici e possono modificare il loro comportamento in modo indipendente. E non importa perché sono inadempienti. Come prova potresti implementare calc1DefaultingToCalc2 come:

double calc1DefaultingToCalc2(){
    try { 
        val = calc2(); 
        if(specialValue(val)){
            val = calc2DefaultingToCalc3WithConvertedException()
        }
    }
    catch (Calc1Exception e1)
    {
        //defaulting to simpler method
        return calc2defaultingToCalc3WithConvertedException();
    }
}

-1

Sembra che i tuoi calcoli abbiano informazioni più valide da restituire rispetto al solo calcolo stesso. Forse avrebbe più senso per loro gestire le eccezioni e restituire una classe "risultati" che contiene informazioni sugli errori, informazioni sui valori, ecc. Pensa come fa la classe AsyncResult seguendo il modello asincrono. È quindi possibile valutare il risultato reale del calcolo. Puoi razionalizzarlo pensando in termini che se un calcolo fallisce, è altrettanto informativo come se avesse successo. Pertanto, un'eccezione è un'informazione, non un "errore".

internal class SomeCalculationResult 
{ 
     internal double? Result { get; private set; } 
     internal Exception Exception { get; private set; }
}

...

SomeCalculationResult calcResult = Calc1();
if (!calcResult.Result.HasValue) calcResult = Calc2();
if (!calcResult.Result.HasValue) calcResult = Calc3();
if (!calcResult.Result.HasValue) throw new NoCalcsWorkedException();

// do work with calcResult.Result.Value

...

Naturalmente, mi chiedo di più sull'architettura complessiva che stai utilizzando per eseguire questi calcoli.


Questo va bene - simile a quanto suggerito da OP per quanto riguarda i calcoli. Preferirei qualcosa di simile while (!calcResult.HasValue) nextCalcResult(), invece di un elenco di Calc1, Calc2, Calc3 ecc.
Kirk Broadhurst

-3

Che ne dici di monitorare le azioni che stai facendo ...

double val;
string track = string.Empty;

try 
{ 
  track = "Calc1";
  val = calc1(); 

  track = "Calc2";
  val = calc2(); 

  track = "Calc3";
  val = calc3(); 
}
catch (Exception e3)
{
   throw new NoCalcsWorkedException( track );
}

4
In che modo questo aiuta? se calc1 () fallisce, cals2 non verrà mai eseguito!
DeCaf

questo non risolve il problema. Esegui solo calc1 se calc2 fallisce, esegui solo calc3 se calc1 && calc2 fallisce.
Jason

+1 orn. Questo è ciò che faccio. Devo solo codificarne uno cattura, il messaggio che mi è stato inviato ( trackin questo caso) e so esattamente quale segmento del mio codice ha causato il fallimento del blocco. Forse avresti dovuto elaborare per dire a membri come DeCaf che il trackmessaggio viene inviato alla tua routine di gestione degli errori personalizzata che ti consente di eseguire il debug del codice. Sembra che non abbia capito la tua logica.
jp2code

Bene, @DeCaf è corretto, il mio segmento di codice non continua a eseguire la funzione successiva che è ciò che ha chiesto jjoelson, lì per la mia soluzione non è fattibile
Orn Kristjansson
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.