Domanda

Ho diversi metodi simili, ad esempio. CalculatePoint (...) e CalculateListOfPoints (...). Occasionalmente, potrebbero non riuscire e devono essere indicati al chiamante. Per CalculateListOfPoints, che restituisce un elenco generico, potrei restituire un elenco vuoto e richiedere al chiamante di verificarlo; tuttavia Point è un tipo di valore e quindi non posso restituire null lì.

Idealmente, vorrei che i metodi fossero "simili" simili; una soluzione potrebbe essere quella di definirli come

public Point CalculatePoint(... out Boolean boSuccess);
public List<Point> CalculateListOfPoints(... out Boolean boSuccess);

o in alternativa per restituire un punto? per CalculatePoint e restituire null per indicare l'errore. Ciò significherebbe dover ricorrere al tipo non annullabile, il che sembra eccessivo.

Un altro percorso sarebbe quello di restituire il boSuccess booleano, avere il risultato (Punto o Elenco) come parametro 'out' e chiamarli TryToCalculatePoint o qualcosa del genere ...

Che cos'è la migliore pratica?

Modifica: non voglio usare Eccezioni per il controllo del flusso! Talvolta è previsto un fallimento.

È stato utile?

Soluzione

Personalmente, penso che userei la stessa idea di TryParse (): usare un parametro out per generare il valore reale e restituire un valore booleano che indica se la chiamata ha avuto successo oppure no

public bool CalculatePoint (... out point point);

Non sono un fan dell'utilizzo dell'eccezione per " normale " comportamenti (se si prevede che la funzione non funzioni per alcune voci).

Altri suggerimenti

Perché dovrebbero fallire? Se è a causa di qualcosa che il chiamante ha fatto (cioè gli argomenti forniti), lanciare ArgumentException è del tutto appropriato. Un metodo Try [...] che evita l'eccezione va bene.

Penso che sia una buona idea fornire la versione che genera un'eccezione, quindi i chiamanti che si aspettano che forniranno sempre dati validi riceveranno un messaggio adeguatamente forte (cioè un'eccezione) se sbagliano.

Un'altra alternativa è lanciare un'eccezione. Tuttavia, in genere desideri solo generare eccezioni in "casi eccezionali".

Se i casi di errore sono comuni (e non eccezionali), hai già elencato le tue due opzioni. MODIFICA : nel tuo progetto potrebbe esserci una convenzione su come gestire tali casi non eccezionali (se si dovrebbe restituire successo o l'oggetto). Se non esiste una convenzione esistente, sono d'accordo con lucasbfr e suggerisco di restituire il successo (che concorda con il modo in cui TryParse (...) è progettato).

Se l'errore è per un motivo specifico, allora penso che sia ok restituire null, o bool e avere un parametro out. Se tuttavia si restituisce null indipendentemente dall'errore, non lo consiglio. Le eccezioni forniscono un ricco set di informazioni, incluso il motivo PERCHÉ qualcosa non ha funzionato, se tutto ciò che ottieni è nullo, allora come fai a sapere se è perché i dati sono sbagliati, hai esaurito la memoria o qualche altro strano comportamento.

Anche in .net TryParse ha un fratello Parse in modo che tu possa ottenere l'eccezione, se lo desideri.

Se fornissi un metodo TrySomething, fornirei anche un metodo Something che ha generato un'eccezione in caso di errore. Quindi tocca al chiamante.

Il modello che ho usato è lo stesso usato da MS con i metodi TryParse di varie classi.

Il tuo codice originale:
public Point CalculatePoint (... out boolean boSuccess);
Elenco pubblico CalculateListOfPoints (... out boolean boSuccess);

Si trasformerebbe in public bool CalculatePoint (... out (o ref) Point CalculatedValue);
public bool CalculateListOfPoints (... out (o ref) Elenco CalculatedValues);

Fondamentalmente si rende il successo / fallimento il valore restituito.

Per riassumere ci sono un paio di approcci che puoi adottare:

  1. Quando il tipo restituito è un tipo valore, come Punto, utilizzare la funzione Nullable di C # e restituire un Punto? (aka Nullable), in questo modo puoi comunque restituire null in caso di errore
  2. Genera un'eccezione in caso di errore. L'intero argomento / discussione su ciò che è e non è "eccezionale" è un punto controverso, è la tua API, decidi qual è il comportamento eccezionale.
  3. Adotta un modello simile a quello implementato da Microsoft nei tipi di base come Int32, fornisci CalculatePoint e TryCalculatePoint (int32.Parse e int32. TryParse) e fai un lancio e uno restituisce un valore.
  4. Restituisce una struttura generica dai tuoi metodi che ha due proprietà, bool Success e GenericType Value.

A seconda dello scenario, tendo a usare una combinazione di restituzione nulla o lancio di un'eccezione come sembrano "più puliti". per me e adattarsi meglio con la base di codice esistente presso l'azienda per cui lavoro. Quindi la mia migliore pratica personale sarebbe l'approccio 1 e 2.

Dipende principalmente dal comportamento dei metodi e dal loro utilizzo.

Se l'errore è comune e non critico, fare in modo che i metodi restituiscano un valore booleano che indica il loro successo e utilizzare un parametro out per trasmettere il risultato. Cercando una chiave in un hash, tentando di leggere i dati su un socket non bloccante quando non sono disponibili dati, tutti questi esempi rientrano in quella categoria.

Se l'errore è imprevisto, restituire direttamente il risultato e trasmettere errori con eccezioni. L'apertura di un file di sola lettura, la connessione a un server TCP, sono buoni candidati.

E a volte entrambi i modi hanno senso ...

Restituisce Point.Empty . È un modello di progettazione .NET restituire un campo speciale quando si desidera verificare se la creazione della struttura ha avuto esito positivo. Evita i parametri out quando puoi.

public static readonly Point Empty

Un modello con cui sto sperimentando sta restituendo un Forse . Ha la semantica del modello TryParse , ma una firma simile al modello null-return-on-error.

Non sono ancora convinto in un modo o nell'altro, ma lo offro per la tua considerazione collettiva. Ha il vantaggio di non richiedere una variabile da definire prima della chiamata del metodo per mantenere il parametro out nel sito di chiamata del metodo. Potrebbe anche essere esteso con una raccolta Errori o Messaggi per indicare il motivo dell'errore.

La classe Maybe è simile a questa:

/// <summary>
/// Represents the return value from an operation that might fail
/// </summary>
/// <typeparam name="T"></typeparam>
public struct Maybe<T>
{
    T _value;
    bool _hasValue;


    public Maybe(T value)
    {
        _value = value;
        _hasValue = true;
    }

    public Maybe()
    {
        _hasValue = false;
        _value = default(T);
    }


    public bool Success
    {
        get { return _hasValue; }
    }


    public T Value
    {
        get 
            { // could throw an exception if _hasValue is false
              return _value; 
            }
    }
}

Direi che la migliore pratica è che un valore restituito significa successo, e un eccezione significa fallimento.

Non vedo alcun motivo negli esempi forniti per cui non dovresti usare eccezioni in caso di guasto.

L'uso di un'eccezione è una cattiva idea in alcuni casi (specialmente quando si scrive un server). Avresti bisogno di due gusti del metodo. Guarda anche la classe del dizionario per un'indicazione di cosa dovresti fare.

// NB:  A bool is the return value. 
//      This makes it possible to put this beast in if statements.
public bool TryCalculatePoint(... out Point result) { }

public Point CalculatePoint(...)
{
   Point result;
   if(!TryCalculatePoint(... out result))
       throw new BogusPointException();
   return result;
}

Il meglio dei due mondi!

Il bool TrySomething () è almeno una pratica, che funziona bene con i metodi di analisi di .net, ma non penso che mi piaccia in generale.

Generare un'eccezione è spesso una buona cosa, anche se non dovrebbe essere usato per situazioni che ci si aspetterebbe che accadano in molte situazioni normali e abbia un costo prestazionale associato.

Restituire null quando possibile è nella maggior parte dei casi ok, quando non si desidera un'eccezione.

Tuttavia - il tuo approccio è un po 'procedurale - che ne dici di creare qualcosa come una classe PointCalculator - prendere i dati richiesti come parametri nel costruttore? Quindi si chiama CalculatePoint su di esso e si accede al risultato tramite le proprietà (proprietà separate per Point e for Success).

Non vuoi lanciare eccezioni quando c'è qualcosa di previsto, dato che @Kevin ha dichiarato che le eccezioni sono per casi eccezionali.

Dovresti restituire qualcosa che ci si aspetta per il 'fallimento', generalmente null è la mia scelta di cattivo ritorno.

La documentazione per il tuo metodo dovrebbe informare gli utenti su cosa aspettarsi quando i dati non vengono calcolati .

Una volta abbiamo scritto un intero Framework in cui tutti i metodi pubblici hanno restituito true (eseguito correttamente) o false (si è verificato un errore). Se avessimo bisogno di restituire un valore, abbiamo usato i parametri di output. Contrariamente alla credenza popolare, questo modo di programmare ha semplificato molto il nostro codice.

Bene con Point, puoi rispedire Point.Empty come valore di ritorno in caso di fallimento. Ora tutto ciò che fa è restituire un punto con 0 per il valore X e Y, quindi se questo può essere un valore di ritorno valido, starei lontano da quello, ma se il tuo metodo non restituirà mai un punto (0,0) , quindi puoi usarlo.

Mi dispiace, ho appena ricordato il tipo Nullable, dovresti guardarlo. Non sono troppo sicuro di quale sia il sovraccarico.

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