Domanda

Immaginare un oggetto che si sta lavorando con una raccolta di altri oggetti ad esso associati, per esempio, l'insieme di Controlli su una WinForm.Si desidera controllare un determinato oggetto, ma nella collezione la collezione non ha un Contains() metodo.Ci sono diversi modi per affrontare questo.

  • Implementare il proprio Contains() metodo ciclicamente tutti gli elementi della collezione per vedere se uno di loro è quello che stai cercando.Questa sembra essere la "best practice" approccio.
  • Recentemente ho trovato un po ' di codice in cui, invece di un ciclo, c'è stato un tentativo di accedere a un oggetto all'interno di una dichiarazione di prova, come segue:
try  
{  
    Object aObject = myCollection[myObject];  
}  
catch(Exception e)  
{  
    //if this is thrown, then the object doesn't exist in the collection
}

La mia domanda è come poveri di una pratica di programmazione non si considera la seconda opzione e perché?Come è il rendimento di esso rispetto ad un ciclo attraverso la raccolta?

È stato utile?

Soluzione

Devo dire che questo è abbastanza cattiva pratica.Mentre alcune persone potrebbero essere felici di dire che un ciclo attraverso la raccolta è meno efficiente per la generazione di un'eccezione, c'è un sovraccarico di lavoro per la generazione di un'eccezione.Vorrei anche chiedere perché si sta utilizzando una collezione di accedere a un elemento chiave quando si potrebbe essere più adatto per l'uso di un dizionario o hashtable.

Il mio problema principale con questo codice, tuttavia, è che, a prescindere dal tipo di eccezione, si sta andando sempre di essere di sinistra, con lo stesso risultato.

Per esempio, un'eccezione potrebbe essere gettati perché l'oggetto non esiste nella collezione, o perchè la collezione è null o perché non è possibile cast myCollect[myObject] per obiettivo uccidere.

Tutte queste eccezioni vengono gestite nello stesso modo, che non può essere la vostra intenzione.

Questi sono un paio di articoli su quando e dove è di solito considerato accettabile per lanciare eccezioni:

In particolare mi piace questa citazione dal secondo articolo:

È importante che le eccezioni sono generata solo quando un imprevisto o voci non si verifica l'attività che impedisce un metodo di completare il suo normale funzione.La gestione delle eccezioni introduce un overhead e abbassa le prestazioni quindi non possono essere utilizzati per il normale flusso del programma, invece di elaborazione condizionale.Può anche essere difficile mantenere il codice abusa la gestione delle eccezioni in questo modo.

Altri suggerimenti

La regola generale è quello di evitare l'uso di eccezioni per il flusso di controllo a meno che le circostanze che attiverà l'eccezione sono "eccezionali", per esempio, estremamente raro!

Se questo è qualcosa che succede normalmente e regolarmente sicuramente non deve essere gestito come un'eccezione.

Le eccezioni sono molto, molto lento a causa di tutte le spese necessarie, quindi ci possono essere motivi di prestazioni pure, se succede abbastanza spesso.

Se, durante la scrittura del codice, si prevede che questo oggetto per essere nella collezione, e poi durante la fase di esecuzione si scopre che non lo è, direi che è un caso eccezionale, e non è corretto usare un'eccezione.

Tuttavia, se si verifica l'esistenza di un oggetto, e si scopre che non c'è, questo non è eccezionale.Usando un'eccezione in questo caso non è corretto.

L'analisi delle prestazioni di runtime dipende dall'effettivo ritiro usato, e il metodo di ricerca.Che non importa se.Non lasciate che l'illusione di ottimizzazione ingannare la scrittura di codice confusionario.

Avrei dovuto pensarci di più di quanto mi piace...il mio istinto è, eh, non così tanto...

EDIT:Ryan Fox commenti su il caso eccezionale è perfetto, sono d'accordo

Come per le prestazioni, dipende l'indicizzatore sulla raccolta.C# consente di sostituire l'indicizzatore operatore, quindi se si sta facendo un ciclo for come il metodo contains scrivete, quindi sarà lento (con forse un paio di nanosecondi lento a causa del try/catch...ma niente di cui preoccuparsi, a meno che il codice è all'interno di una vasta ansa).

Se l'indicizzatore è O(1) (o anche O(log(n))...o qualcosa di più veloce di un tempo O(n)), allora il try/catch soluzione potrebbe essere più veloce di corso.

Inoltre, sto assumendo l'indicizzatore è il lancio di un'eccezione, se è restituire null, ovviamente potreste controllare per nulla e di non usare il try/catch.

In generale, utilizzando la gestione delle eccezioni per il flusso del programma di logica e di cattiva pratica.Io personalmente credo che la seconda opzione è inaccettabile l'uso di eccezioni.Date le caratteristiche di lingue comunemente usato in questi giorni (come Linq e le espressioni lambda in C# per esempio) non c'è nessun motivo per non scrivere il proprio metodo Contains ().

Come un pensiero finale, in questi giorni la maggior parte delle collezioni fare un metodo contains già.Quindi penso che per la maggior parte, questo è un non-problema.

Le eccezioni devono essere eccezionali.

Qualcosa come 'La collezione è mancante perché il database è caduto di sotto e' eccezionale

Qualcosa come "la chiave non è presente' è un comportamento normale per un dizionario.

Per il tuo esempio specifico di una windows form di raccolta del Controllo, il Controls la struttura dispone di un ContainsKey il metodo, che è ciò che si dovrebbe utilizzare.

Non c'è ContainsValue perché quando si tratta con i dizionari/hashtable, non c'è modo più veloce breve di scorrere l'intera collezione, per controllare se c'è qualcosa, così sei davvero scoraggiato dal farlo.

Del MOTIVO per cui le Eccezioni dovrebbero essere eccezionali, si tratta di 2 cose

  1. Che indica che il codice sta cercando di fare.Si desidera avere il vostro codice di corrispondere a ciò che si sta cercando di ottenere, per quanto possibile, in modo che sia leggibile e mantenibile.La gestione delle eccezioni aggiunge un sacco di extra cruft che ottiene nel modo di questo scopo

  2. La brevità del codice.Vuoi che il tuo codice per fare quello che sta facendo nel modo più diretto, quindi è leggibile e mantenibile.Di nuovo, il cruft aggiunto dalla gestione delle eccezioni ottiene nel modo di questo.

Date un'occhiata a questo post del blog di Krzystof: http://blogs.msdn.com/kcwalina/archive/2008/07/17/ExceptionalError.aspx

Le eccezioni devono essere utilizzati per comunicare le condizioni di errore, ma non dovrebbe essere usato come logica di controllo (soprattutto quando ci sono molto modi più semplici per determinare una condizione, come la Contiene).

Parte del problema è che le eccezioni, mentre non costoso buttare sono costosi cattura e tutte le eccezioni che sono catturati a un certo punto.

Quest'ultima è una soluzione accettabile.Anche se mi sarebbe sicuramente cattura l'eccezione specifica (ElementNotFound?) che la collezione si getta in questo caso.

Speedwise, dipende dal comune.Se siete più probabilità di trovare l'elemento che non, eccezione per la soluzione più veloce.Se siete più probabilità di fallire, poi dipenderà dalla dimensione della collezione e la sua iterazione velocità.In ogni modo, vuoi per misurare contro l'uso normale per vedere se questo è in realtà un collo di bottiglia prima di preoccuparsi di questa velocità.Andare per chiarezza prima e la seconda soluzione è molto più chiaro rispetto al primo.

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