Domanda

Ho appena iniziato a sfogliare "Debug delle applicazioni MS .Net 2.0" di John Robbins e sono rimasto confuso dalla sua evangelizzazione per Debug.Assert(...).

Sottolinea che gli Assert ben implementati memorizzano in qualche modo lo stato di una condizione di errore, ad esempio:

Debug.Assert(i > 3, "i > 3", "This means I got a bad parameter");

Ora, personalmente, mi sembra folle che adori così tanto riaffermare il suo test senza un commento di "logica aziendale" sensato, forse "i <= 3 non deve mai accadere a causa del processo di widgitificazione flobittyjam".

Quindi, penso di ricevere Assert come una sorta di cosa di basso livello "Proteggiamo le mie ipotesi"...supponendo che si ritenga che questo sia un test che è necessario eseguire solo in fase di debug, ad es.ti stai proteggendo dai colleghi e dai futuri programmatori e speri che testino effettivamente le cose.

Ma quello che non capisco è che poi prosegue dicendo che dovresti usare le asserzioni oltre alla normale gestione degli errori;ora quello che immagino è qualcosa del genere:

Debug.Assert(i > 3, "i must be greater than 3 because of the flibbity widgit status");
if (i <= 3)
{
    throw new ArgumentOutOfRangeException("i", "i must be > 3 because... i=" + i.ToString());
}

Cosa ho guadagnato dalla ripetizione Debug.Assert del test della condizione di errore?Penso che lo capirei se stessimo parlando del doppio controllo di solo debug di un calcolo molto importante...

double interestAmount = loan.GetInterest();
Debug.Assert(debugInterestDoubleCheck(loan) == interestAmount, "Mismatch on interest calc");

...ma non capisco per i test dei parametri che vale sicuramente la pena controllare (sia nelle build DEBUG che in quelle di rilascio)...o no.Cosa mi manca?

È stato utile?

Soluzione

Le asserzioni non servono per il controllo dei parametri.Il controllo dei parametri dovrebbe essere sempre effettuato (e precisamente in base alle precondizioni specificate nella documentazione e/o nelle specifiche) e il ArgumentOutOfRangeException lanciato secondo necessità.

Le asserzioni servono per testare situazioni "impossibili", cioè cose che tu (nella logica del tuo programma) assumere sono vere.Le asserzioni sono lì per dirti se questi presupposti vengono violati per qualsiasi motivo.

Spero che questo ti aiuti!

Altri suggerimenti

Esiste un aspetto comunicativo tra le asserzioni e il lancio di eccezioni.

Supponiamo di avere una classe User con una proprietà Name e un metodo ToString.

Se ToString è implementato in questo modo:

public string ToString()
{
     Debug.Assert(Name != null);
     return Name;
}

Dice che Name non dovrebbe mai essere nullo e, se lo è, c'è un bug nella classe User.

Se ToString è implementato in questo modo:

public string ToString()
{
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

Dice che il chiamante utilizza ToString in modo errato se Name è null e dovrebbe verificarlo prima di chiamare.

L'implementazione con entrambi

public string ToString()
{
     Debug.Assert(Name != null);
     if ( Name == null )
     {
          throw new InvalidOperationException("Name is null");
     }

     return Name;
}

dice che se Name è null c'è un bug nella classe User, ma vogliamo gestirlo comunque.(L'utente non ha bisogno di controllare il nome prima di chiamare.) Penso che questo sia il tipo di sicurezza che Robbins raccomandava.

Ci ho pensato a lungo e intensamente quando si tratta di fornire indicazioni su debug vs.affermare rispetto alle preoccupazioni relative ai test.

Dovresti essere in grado di testare la tua classe con input errati, cattivo stato, ordine delle operazioni non valido e qualsiasi altra condizione di errore immaginabile e un'asserzione dovrebbe Mai viaggio.Ogni asserzione sta controllando qualcosa che dovrebbe Sempre essere vero indipendentemente dagli input o dai calcoli eseguiti.

Buone regole pratiche a cui sono arrivato:

  1. Le asserzioni non sostituiscono un codice robusto che funziona correttamente indipendentemente dalla configurazione.Sono complementari.

  2. Le asserzioni non dovrebbero mai essere attivate durante l'esecuzione di un test dell'unità, anche quando si immettono valori non validi o si verificano condizioni di errore.Il codice dovrebbe gestire queste condizioni senza che si verifichi un'asserzione.

  3. Se un'asserzione scatta (in un test unitario o durante il test), la classe viene disturbata.

Per tutti gli altri errori, in genere dovuti all'ambiente (connessione di rete persa) o all'uso improprio (il chiamante ha passato un valore nullo), è molto più bello e comprensibile utilizzare controlli ed eccezioni rigidi.Se si verifica un'eccezione, il chiamante sa che probabilmente è colpa sua.Se si verifica un'asserzione, il chiamante sa che probabilmente si tratta di un bug nel codice in cui si trova l'asserzione.

Per quanto riguarda la duplicazione:Sono d'accordo.Non vedo perché dovresti replicare la convalida con un Debug.Assert E un controllo delle eccezioni.Non solo aggiunge un po’ di rumore al codice e confonde le acque riguardo a chi sia la colpa, ma è una forma di ripetizione.

Utilizzo controlli espliciti che generano eccezioni pubblico E protetto metodi e asserzioni su metodi privati.

Di solito, i controlli espliciti impediscono comunque ai metodi privati ​​di vedere valori errati.Quindi, in realtà, l'asserzione sta verificando una condizione che dovrebbe essere impossibile.Se un'asserzione si attiva, mi dice che c'è un difetto nella logica di validazione contenuta in una delle routine pubbliche della classe.

Un'eccezione può essere catturata e inghiottita rendendo l'errore invisibile ai test.Ciò non può accadere con Debug.Assert.

Nessuno dovrebbe mai avere un gestore delle catture che catturi tutte le eccezioni, ma le persone lo fanno comunque e talvolta è inevitabile.Se il tuo codice viene richiamato da COM, il livello di interoperabilità rileva tutte le eccezioni e le trasforma in codici di errore COM, il che significa che non vedrai le eccezioni non gestite.Gli asserti non ne soffrono.

Anche quando l'eccezione non viene gestita, una pratica ancora migliore è eseguire un mini-dump.Un'area in cui VB è più potente di C# è che puoi utilizzare un filtro delle eccezioni per creare un mini-dump quando l'eccezione è in volo e lasciare invariato il resto della gestione delle eccezioni. Post del blog di Gregg Miskelly sull'inserimento del filtro delle eccezioni fornisce un modo utile per eseguire questa operazione da C#.

Un'altra nota sugli asset...interagiscono male con il test unitario delle condizioni di errore nel codice.Vale la pena avere un wrapper per disattivare l'asserzione per i test unitari.

IMO è solo una perdita di tempo di sviluppo.L'eccezione implementata correttamente fornisce un quadro chiaro di ciò che è accaduto.vidi troppo applicazioni che mostrano l'oscuro "Asserzione non riuscita:i < 10" errori.Vedo l'affermazione come una soluzione temporanea.A mio parere nessuna asserzione dovrebbe essere presente nella versione finale di un programma.Nella mia pratica ho utilizzato le asserzioni per controlli rapidi e sporchi.La versione finale del codice dovrebbe tenere conto della situazione errata e comportarsi di conseguenza.Se succede qualcosa di brutto hai 2 scelte:gestiscilo o lascialo.La funzione dovrebbe lanciare un'eccezione con una descrizione significativa se vengono passati parametri errati.Non vedo punti nella duplicazione della logica di convalida.

Esempio di buon utilizzo di Assert:

Debug.Assert(flibbles.count() < 1000000, "too many flibbles"); // indicate something is awry
log.warning("flibble count reached " + flibbles.count()); // log in production as early warning

Personalmente penso che Assert dovrebbe soltanto essere usato quando sai che c'è qualcosa fuori auspicabile limiti, ma puoi star certo che è ragionevolmente sicuro continuare.In tutte le altre circostanze (sentiti libero di indicare circostanze a cui non ho pensato) usa le eccezioni per fallire duramente e velocemente.

Il compromesso chiave per me è se vuoi disattivare un sistema live/di produzione con un'eccezione per evitare la corruzione e semplificare la risoluzione dei problemi, o se hai riscontrato una situazione che non dovrebbe mai continuare inosservata nelle versioni di test/debug ma che potrebbe essere autorizzato a continuare la produzione (registrando un avviso ovviamente).

cfr. http://c2.com/cgi/wiki?FailFastcopiato e modificato dalla domanda Java: Eccezione vs asserzione

Ecco di 2 centesimi.

Penso che il modo migliore sia usare sia le asserzioni che le eccezioni.Le principali differenze tra i due metodi, secondo me, sono che le istruzioni Assert possono essere rimosse facilmente dal testo dell'applicazione (definisce, attributi condizionali...), mentre le Exception lanciate dipendono (tipicamente) da un codice condizionale che è più difficile da rimuovere ( sezione multine con condizionali del preprocessore).

Ogni eccezione dell'applicazione dovrà essere gestita correttamente, mentre le asserzioni dovranno essere soddisfatte solo durante lo sviluppo e il test dell'algoritmo.

Se si passa un riferimento a un oggetto nullo come parametro di routine e si utilizza questo valore, si ottiene un'eccezione del puntatore nullo.Infatti:perché dovresti scrivere un'affermazione?E' una perdita di tempo in questo caso.Ma che dire dei membri della classe privata utilizzati nelle routine di classe?Quando questi valori sono impostati da qualche parte, è meglio verificare con un'asserzione se è impostato un valore nullo.Questo è solo perché quando usi il membro, ottieni un'eccezione del puntatore null ma non sai come è stato impostato il valore.Ciò causa un riavvio del programma interrotto durante l'utilizzo di tutti i punti di ingresso per impostare il membro privato.

Le eccezioni sono più utili, ma possono essere (imho) molto pesanti da gestire e c'è la possibilità di utilizzare troppe eccezioni.E richiedono controlli aggiuntivi, magari indesiderati, per ottimizzare il codice.Personalmente utilizzo le eccezioni solo ogni volta che il codice richiede un controllo approfondito del catch (le istruzioni catch sono molto basse nello stack di chiamate) o ogni volta che i parametri della funzione non sono codificati nel codice.

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