2013-05-14 12 views
5

Sto lavorando all'interno di un metodo che esegue una serie di controlli di convalida e nel caso in cui uno di questi controlli fallisce chiama uno Action<string> per eseguire un codice di rifiuto comune. Il qualcosa di set up è simile a questo:Ritorno dal metodo genitore all'interno di Azione <string>

public void ValidationMethod() { 
    Action<string> rejectionRoutine = (rejectionDescription) => { 
     // do something with the reject description 
     // other common code 
    }; 

    if (condition != requiredValue) { 
     rejectionRoutine("Condition check failed"); 
     // I currently have to put `return` here following every failed check 
    } 

    // many more checks following this 
} 

In questo sistema una volta che si verifica non è riuscita la convalida non ho bisogno di convalidare il resto, voglio solo per eseguire il codice comune rifiuto dentro l'azione e uscire il metodo . Attualmente per fare questo ho appena return nella riga successiva a seguito di una chiamata a rejectionRoutine. Mi chiedo se c'è un modo per incorporare la possibilità di restituire o terminare l'esecuzione del metodo genitore dall'interno dello Action?

So che questo è un po 'pignolo, ma mi sento come se fosse meglio per l'estensibilità in fondo alla strada se qualcun altro ha bisogno di aggiungere ulteriori controlli di convalida (non dovranno preoccuparsi di rimandare indietro tutto il luogo) e incapsulare il comportamento comune di terminare l'esecuzione all'interno del codice che dovrebbe essere comune a questi casi.

risposta

6

Un modo di pulizia del codice di un po 'sarebbe quello di estrapolare tutti i controlli fuori in una collezione:

Dictionary<Func<bool>, string> checks = new Dictionary<Func<bool>, string>() 
{ 
    {()=> condition != requiredValue, "Condition check failed"}, 
    {()=> otherCondition != otherRequiredValue, "Other condition check failed"}, 
    {()=> thirdCondition != thirdRequiredValue, "Third condition check failed"}, 
}; 

Se è importante per i controlli da eseguire in un ordine particolare (questo codice è un ordine imprevedibile), quindi si consiglia di utilizzare qualcosa come un List<Tuple<Func<bool>, string>> invece.

var checks = new List<Tuple<Func<bool>, string>>() 
{ 
    Tuple.Create<Func<bool>, string>(()=> condition != requiredValue 
     , "Condition check failed"), 
    Tuple.Create<Func<bool>, string>(()=> otherCondition != otherRequiredValue 
     , "Other condition check failed"), 
    Tuple.Create<Func<bool>, string>(()=> thirdCondition != thirdRequiredValue 
     , "Third condition check failed"), 
}; 

È quindi possibile utilizzare LINQ per fare la convalida:

var failedCheck = checks.FirstOrDefault(check => check.Item1()); 
if (failedCheck != null) 
    rejectionRoutine(failedCheck.Item2); 
+0

Ahhh Servy, le grandi menti pensano allo stesso modo.Ho scritto un codice che era quasi identico a questo prima di trovare un paio di punti in cui non aveva senso. Vi sono alcuni controlli che generano eccezioni se vengono valutati quando un controllo precedente deve essere teriminato. Vale a dire. check1 è value! = null, check2 potrebbe fare qualcosa con valore assumendo che non può essere nullo se siamo arrivati ​​fin qui –

+0

@JesseCarter Quindi, come ho detto alla fine, dovrai usare un 'Elenco , stringa >> '. È solo un po 'più codice. – Servy

+0

Forse sono confuso riguardo al modo in cui verrà valutato il bool in Func? La valutazione effettiva del valore bool viene posticipata finché non viene richiamato Func? Supponevo che sarebbe stato valutato non appena è stato creato il Func –

1

Non ha senso restituire dal metodo chiamante all'interno di un'espressione lambda.
(cosa succede se si chiama dopo che il metodo è finito in esecuzione?)

Invece, è possibile cambiarlo in un Func<string, Whatever> e restituire il suo valore:

return rejectionRoutine("Condition check failed"); 
+0

L'azione vive a livello locale all'interno del metodo in modo non vedo come potrebbe essere chiamato se il metodo fosse finito in esecuzione? Se restituissi qualcosa da un Func, dovrei comunque fare un if (returnValue == something) return dopo ogni controllo. O mi sto perdendo qualcosa con questo approccio? –

+0

@JesseCarter: Niente ti impedisce di esporre il delegato al di fuori del metodo. Non sono sicuro di cosa intendi. – SLaks

+0

Ho modificato l'esempio di codice nella mia domanda, l'azione esiste solo nell'ambito del metodo di convalida circostante. Non vedo come si possa chiamare ovunque tranne che all'interno di quel metodo. –

0

Come alternativa alla soluzione di SLaks è anche possibile genera un'eccezione nel tuo rejectionRoutine se il tuo design lo consente.

+0

Purtroppo non credo che il lancio di un'eccezione corrisponda al design attuale. Grazie per il pensiero però –

+3

Le eccezioni non dovrebbero essere usate per il normale flusso di controllo; dovrebbero essere usati per situazioni eccezionali. – Servy

+0

I miei pensieri esattamente Servy e sono sicuro che sarebbe il consenso nella mia recensione del codice se provassi a farlo in quel modo –

Problemi correlati