Restituzione di una bandiera che indica il successo [chiuso]

4

Ogni volta che sto scrivendo il codice, cancello sempre fuori i miei metodi come questo (non necessariamente usando i generici):

public T MyMethod()
{
    T result = default(T); // or null

    return result;
}

Questo mi mette sempre nel posto giusto per compilare la logica e assicurarmi di avere una variabile di ritorno da tracciare durante il processo. Dopo averlo fatto abbastanza a lungo ho trovato un modello di sviluppo, e non sono sicuro di quale direzione prendere. Di solito in alcune operazioni relativamente banali:

public bool AddRecord(Record r)
{
    bool result = false;

    if(_collection != null)
    {
        _collection.Add(r);

        result = true;
    }

    return result;
}

Per inciso, in genere assegnerei il risultato del callee interno a result ma alcuni metodi (come Add ) non hanno alcun risultato.

Questo metodo, così com'è, va bene. Ma per alcuni sistemi, lo scriverei un po 'diverso.

public bool AddRecord(Record r)
{
    bool result = false;

    if(_collection == null)
    {
        _collection = new List<Record>();
    }

    _collection.Add(r);

    result = true;

    return result;
}

Ed è qui che rifletto su cosa dovrei fare. Il ritorno sempre di true non fornisce alcun valore all'utente finale, secondo me. Ma poi dico: "Beh, se in futuro questo metodo diventasse più complesso, vorresti cambiare la firma del metodo e far sì che tutti i chiamanti aggiornino la loro reazione a questo? O preferiresti solo return true per ora? " a cui tendo a preferire quest'ultimo, a meno che non sia qualcosa di così banalmente semplice che ho molta fiducia che non cambierà (che, di per sé, ha il 50% di possibilità di sbagliare). Un'altra opzione sarebbe supporre che, sebbene questa logica interna non possa generare una condizione falsa, potrebbe generare un'eccezione. Il mio metodo dovrebbe gestirlo e restituire un flag che indica se è stata lanciata un'eccezione? Pensando a questo, penso:

  • Mangiare le eccezioni è male, e a meno che non possa fare qualcosa con le informazioni che mi sono state date, non dovrei farlo e dovrei lasciare che sia il chiamante a gestirle.
  • Il chiamante si interessa anche di questa eccezione, o si preoccupano solo se questa operazione è riuscita nel suo insieme?

Questo mi porta a questo stato in cui ho un metodo in cui non sono sicuro di cosa fare, quindi in genere restituisco una costante true e non consumo l'eccezione.

La mia domanda è: Nel caso generale, qual è il modo migliore per avvicinarsi a questo?

Capisco che i casi specifici richiedano una considerazione specifica e, mentre scrivo il mio codice, considero sempre il caso unico presentato da ciascun metodo. Ma quando mi rendo conto che un metodo non è più unico di un altro, vorrei avere qualche idea in più su cosa dovrei fare. Molte grazie in anticipo per la tua opinione.

    
posta Will Custode 20.07.2015 - 20:39
fonte

3 risposte

3

Secondo me, i metodi dovrebbero restituire un valore o avere un effetto collaterale, ma non entrambi.

In questa risposta ad un'altra domanda l'autore cita la fonte di tale pratica.

Nei tuoi esempi, gestisci una situazione in cui cerchi di aggiungere un elemento a una raccolta che potrebbe essere o non essere inizializzata. Il mio suggerimento è di lanciare un'eccezione se la raccolta non è inizializzata, o inizializzarla se è nullo e quindi aggiungere l'elemento, ma non restituire un valore.

In questa risposta a un'altra domanda puoi vedere gli svantaggi dei codici di ritorno rispetto alle eccezioni.

Per riassumere:

  • I codici di ritorno sono più dettagliati
  • I codici di ritorno sono più fragili
  • I codici restituiti devono a volte essere tradotti
  • Codici di ritorno significa che non puoi concatenare espressioni
  • Le eccezioni sono state digitate
  • Le eccezioni sono ... sincrone
risposta data 20.07.2015 - 20:55
fonte
2

Hai ragione che è generalmente meglio lasciare che le eccezioni di altri codici si propagino attraverso il tuo senza impedimenti. Dove lavoro, questo è chiamato "eccezionalmente agnostico".

Ora per la parte difficile. I valori di ritorno delle tue funzioni fanno parte della tua API pubblica. Avere un valore di ritorno che non significa nulla è come avere un metodo che non fa nulla quando viene chiamato. Non dovrebbe essere lì.

Ancora più importante, diciamo che hai pubblicato una funzione che è sempre tornata vera perché non poteva fallire. Poi un giorno lo cambi in modo che possa fallire, e la funzione a volte ritorna falsa. Potresti non aver cambiato la firma della funzione, ma hai cambiato il contratto . Di conseguenza, tutto il codice esistente che utilizza quella funzione sarà probabilmente danneggiato, perché è stato scritto contro il vecchio contratto "questa funzione non può fallire". Potrebbe essere molto peggio che non riuscire semplicemente a compilare.

Quindi fai corrispondere la firma al contratto. Se non può fallire, non restituire un valore di successo / fallimento. Qualsiasi altra cosa causa solo confusione, o la paura che tu possa fare cambiamenti all'indietro-incompatibili un giorno o l'altro. Quando hai bisogno di fare un enorme cambiamento semantico come l'introduzione della possibilità di fallire, questo deve entrare in una funzione completamente nuova.

Per inciso, quando alcuni metodi possono fallire in una varietà di modi significativamente diversi, è spesso una buona idea raggrupparli in un unico metodo in modo da poter restituire un singolo enum che descrive ogni possibile tipo di errore. Un tipico esempio potrebbe essere la funzione executeQuery () per una libreria di database.

P.S. Penso che AddRecord sarebbe più leggibile con un ritorno anticipato ( if(_collection == null) { return false; } ) ma forse sono solo io.

    
risposta data 20.07.2015 - 21:00
fonte
0

Se trovassi un metodo chiamato AddRecord , concluderei - dal suo nome - che si trattava di una dichiarazione, non di una domanda si / no. Non mi sta chiedendo se voglio aggiungere un record; Lo sto dicendo di farlo. Quindi mi aspetto uno dei due possibili tipi di ritorno, a seconda che stia pensando a OO o funzionale:

  • void - Gli ho comandato di fare qualcosa. Se c'è un problema, genererà un'eccezione.
  • ICollectionOfSomeSort - Ho chiesto di aggiungere la mia collezione, quindi mi aspetto una raccolta indietro (idealmente una nuova lettura di sola lettura).

Ottenere un boolean indietro non ha molto senso. Un true potrebbe rassicurarmi sul fatto che ha funzionato, ma cosa significa false ? "Non potrebbe essere disturbato"? "Scusa, non aggiungo alle raccolte di venerdì"? Non viene fornito alcun motivo di errore; solo un "computer dice no" risposta.

Tuttavia, è necessario prestare attenzione a quando e perché verrà generata un'eccezione. In vari esempi, stai testando lo stato di _collection . In un caso si restituisce false se è null . Altre risposte hanno suggerito di dover fare un'eccezione in questo caso. Ciò implica che è responsabilità del chiamante inizializzare lo stato interno della classe prima di chiamare AddRecord e che non riuscirà la chiamata se non lo fa. Questa è un'astrazione che perde: stai esponendo il funzionamento interno della tua classe attraverso questi messaggi di errore. Qualsiasi installazione interna come questa dovrebbe accadere senza che l'utente API debba avviarla. Se si verifica un NullReferenceException , dovrebbe indicare un bug nel codice, cioè dovrebbe sempre essere solo colpa tua (come creatore dell'API), piuttosto che il mio (come utente API).

    
risposta data 20.07.2015 - 23:51
fonte

Leggi altre domande sui tag