Domanda

Quando è opportuno utilizzare un ThrowHelper metodo invece di buttare direttamente?

void MyMethod() {
    ...
    //throw new ArgumentNullException("paramName");
    ThrowArgumentNullException("paramName");
    ...
}
void ThrowArgumentNullException(string paramName) {
    throw new ArgumentNullException(paramName);
}

Ho letto che chiamare un ThrowHelper metodo (un metodo con l'unico purpouse di un'eccezione) invece di buttare direttamente dovrebbe cedere più piccolo bytecode.

Questo, e l'incapsulamento evidente (un altro livello di riferimento indiretto), può essere buone ragioni per non gettare direttamente, almeno in alcuni scenari.

In ogni caso, IMO gli svantaggi non sono inconsistenti troppo.

  • Una parte del ( eccezionale ) flusso di controllo è nascosto
  • Eccezioni finiscono per avere uno stacktrace più criptico
  • il compilatore (2.0) non riconoscerà che ThrowHelper chiamate sono punti di uscita da un metodo, quindi codice-around è necessario.

La mia esperienza limitata è che spesso il disegno complessivo peggiora.

int MyMethod(int i) {
    switch (i) {
        case 1:
            return 1;
        default:
            ThrowMyException();
    }
    return 0; // Unreachable (but needed) code
 }

Questo può essere in parte una questione di gusto personale. In ogni caso, quali sono le vostre linee guida personali su questo problema? Ti sembra una buona idea usare ThrowHelpers per tutte quelle attività comuni, come la validazione del metodo param ( ThrowArgumentNullException (paramName) e simili)? Mi sto perdendo qualcosa di ovvio su questo tema?

A proposito sto cercando di non mescolare la questione con il problema di convalida , per esempio un metodo come:

ThrowIfNameIsNullOrEmpty(name);
È stato utile?

Soluzione

Il mio approccio di default è quello di gettare direttamente dal ramo di codice eccezionale.

Il principio DRY e dello stato di 3 guide quando avrei avvolgere che in un metodo: se mi ritrovo a scrivere lo stesso codice di 'tiro' 3 o più volte, Considero avvolgendolo in un metodo di supporto

.

Tuttavia, invece di un metodo che genera, è molto meglio di scrivere un metodo factory che crea l'eccezione desiderata e poi gettarlo dal luogo originale:

public void DoStuff(string stuff)
{
    // Do something

    throw this.CreateException("Boo hiss!");
}

private MyException CreateException(string message)
{
    return new MyException(message);
}

Questo conserva la traccia dello stack.

Altri suggerimenti

Questo mi sembra un'astrazione manco a me. Si dispone di un metodo che fa una cosa, nominato come verbosely come la cosa che lo fa.

L'argomento circa meno bytecode è praticamente priva di significato poiché la dimensione del codice conta raramente (non sarebbe un risparmio di oltre un kilobyte per ogni istanza del programma, a meno che non si passi che un'eccezione da un numero ridicolo di posti nell'origine codice). Nel frattempo i vostri svantaggi indicati sono tutte le preoccupazioni reali, specialmente dove la chiarezza della gestione delle eccezioni è interessato.

In sostanza astraendo cose minori come questo di solito torna a mordere. (Qualche lettura divertente sul tema: http://www.joelonsoftware.com/articles/LeakyAbstractions.html )

Direi che l'unica volta che ragionevole per fare questo è in qualcosa di simile BCL in cui la quantità di utilizzo della classe è molto diffuso sufficiente che il risparmio può essere valsa la pena. Avrebbe dovuto avere un senso, tuttavia. Nel tuo caso, non credo che in realtà stai risparmiando qualsiasi spazio. Le istanze di ThrowHelper nel BCL ridurre le dimensioni sostituendo le enumerazioni per archi e chiamate di metodo (per ottenere i messaggi ad arco). Il tuo caso passa semplicemente gli stessi argomenti e quindi non ottenere alcun risparmio. Il costo è altrettanto per chiamare il metodo come fa a generare l'eccezione al suo posto.

cerco sempre di gettare le mie eccezioni per il solo scopo di personalizzazione. Posso lanciare un messaggio che sta per dirmi in breve tempo quale sia il problema (e, eventualmente, dove ha avuto origine da). Per quanto riguarda i problemi di prestazioni vanno, cerco di limitare il numero di eccezioni sollevate sulle versioni di rilascio del mio software, per quanto possibile, in modo da non ho mai davvero pensato troppo. Sono interessato a vedere ciò che gli altri dicono però.

Questo è il mio 2 centesimi. Si prega di prendere come tale.

Da quello che ho capito, il bytecode più piccolo è praticamente l'unico vantaggio ( Vedere questa domanda ), quindi non aspettatevi vedrete un sacco di argomenti a favore nelle risposte qui.

Un vantaggio non ancora menzionato per mezzo di un tiro-helper o un metodo eccezioni in fabbrica è la possibilità di passare un delegato a tale scopo. Quando si utilizza un delegato tiro-helper, si guadagna la possibilità di non buttare (che in alcuni casi, essere una buona cosa, in altri casi, una brutta cosa). Ad esempio:

string TryGetEntry(string key, Funct<problemCause, string> errorHandler)
{
  if (disposed)
    return errorHandler(problemCause.ObjectDisposed);
  else if (...entry_doesnt_exist...)
    return errorHandler(problemCause.ObjectNotFound);
  else
    return ...entry...;
}
string GetEntry(string key)
{
  return TryGetEntry(key, ThrowExceptionOnError);
}
bool TryGetEntry(string key, ref result)
{
  bool ok;
  string result;
  result = TryGetEntry(key, (problemCause theProblem) => {ok=false; return (string)null;});
  return ok;
}

Con questo approccio, si può facilmente utilizzare una routine con una varietà di strategie di gestione degli errori. Si potrebbe eliminare la necessità di una chiusura avendo il metodo TryGetEntry accettare un parametro di tipo generico insieme ad un parametro 'ref' di quel tipo e un delegato che accetta un parametro come 'ref'; l'esempio è più semplice, però, solo con una chiusura.

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