Come evitare di lanciare eccezioni fastidiose?

21

La lettura di articolo sulle eccezioni di Eric Lippert è stata sicuramente una aprire gli occhi su come dovrei affrontare le eccezioni, sia come produttore che come consumatore. Tuttavia, sto ancora lottando per definire una linea guida su come evitare di lanciare eccezioni irritanti.

In particolare:

  • Supponiamo di avere un metodo di salvataggio che può fallire perché a) Qualcun altro ha modificato il record prima di , o b) il valore che stai tentando di creare esiste già . Queste condizioni sono da aspettarsi e non eccezionali, quindi, invece di lanciare un'eccezione, decidi di creare una versione Try del tuo metodo, TrySave, che restituisce un valore booleano che indica se il salvataggio è riuscito. Ma se fallisce, come farà il consumatore a sapere qual è il problema? O sarebbe meglio restituire un enum indicante il risultato, tipo Ok / RecordAlreadyModified / ValueAlreadyExists? Con integer.TryParse questo problema non esiste, poiché esiste un solo motivo per il quale il metodo può fallire.
  • L'esempio precedente è davvero una situazione irritante? O fare un'eccezione in questo caso sarebbe il modo preferito? So che è come è fatto nella maggior parte delle librerie e dei framework, incluso il framework Entity.
  • Come decidi quando creare una versione di prova del tuo metodo e fornire un modo per testare in anticipo se il metodo funzionerà o no? Attualmente sto seguendo queste linee guida:
    • Se esiste la possibilità di una condizione di competizione, quindi crea una versione Prova. Ciò impedisce al consumatore di rilevare un'eccezione esogena. Ad esempio, nel metodo Save descritto in precedenza.
    • Se il metodo per testare la condizione praticamente farebbe tutto ciò che fa il metodo originale, quindi creare una versione Try. Ad esempio, integer.TryParse ().
    • In qualsiasi altro caso, crea un metodo per testare la condizione.
posta Mike 06.02.2012 - 16:40
fonte

3 risposte

24

Suppose you have a Save method that can fail because a) Somebody else modified the record before you, or b) The value you're trying to create already exists. These conditions are to be expected and not exceptional, so instead of throwing an exception you decide to create a Try version of your method, TrySave, which returns a boolean indicating if the save succeeded. But if it fails, how will the consumer know what was the problem?

Buona domanda.

La prima domanda che mi viene in mente è: se i dati sono già lì, allora in che senso il salvataggio fallisce ? sicuramente sembra che sia riuscito a me . Ma assumiamo per il gusto di argomentare che hai davvero molte ragioni diverse per cui un'operazione può fallire.

La seconda domanda che mi viene in mente è: è l'informazione che desideri restituire all'utente utilizzabile ? Cioè, stanno andando a prendere delle decisioni basate su quelle informazioni?

Quando si accende la spia del "controllo motore", apro il cofano, verifica che nella mia auto non sia presente un motore che non sia in fiamme e lo porti in garage. Ovviamente nel garage hanno tutti i tipi di apparecchiature diagnostiche per scopi speciali che indicano loro perché la spia del motore di controllo è accesa, ma dalla prospettiva mia , il sistema di allarme è ben progettato. Non mi interessa se il problema è perché il sensore di ossigeno sta registrando un livello anormale di ossigeno nella camera di combustione, o perché il rilevatore del minimo è scollegato, o qualsiasi altra cosa. Prenderò la stessa azione, cioè lascia che qualcun altro lo capisca .

Il chiamante si preoccupa del perché il salvataggio non è riuscito? Faranno qualcosa a riguardo, a parte rinunciare o riprovare?

Supponiamo, a ragion veduta, che il chiamante intraprenda azioni diverse a seconda del motivo per cui l'operazione ha avuto esito negativo.

La terza domanda che ci viene in mente è: è la modalità di errore eccezionale ? Penso che potresti confondere possibile con unexceptional . Penserei a due utenti che tentano di modificare lo stesso record contemporaneamente a una situazione eccezionale ma possibile, non a una situazione comune.

Supponiamo, a ragion veduta, che non sia eccezionale.

La quarta domanda che ci viene in mente è: esiste un modo per rilevare in modo affidabile la brutta situazione prima del tempo?

Se la brutta situazione è nel mio secchio "esogeno", allora, no. Non c'è modo di dire in modo affidabile "un altro utente ha modificato questo record?" perché potrebbero modificarlo dopo aver fatto la domanda . La risposta è stantio non appena viene prodotta.

La quinta domanda che ci viene in mente è: c'è un modo per progettare l'API in modo che la situazione brutta possa essere prevenuta?

Ad esempio, è possibile fare in modo che l'operazione "salva" richieda due passaggi. Fase 1: acquisire un blocco sul record che viene modificato. Quell'operazione ha esito positivo o negativo e quindi può restituire un booleano. Il chiamante può quindi avere una politica su come affrontare il fallimento: attendere un po 'e riprovare, rinunciare, qualunque cosa. Fase due: una volta acquisito il blocco, eseguire il salvataggio e rilasciare il blocco. Ora il di salvataggio ha sempre successo e quindi non è necessario preoccuparsi di alcun tipo di gestione degli errori. Se il salvataggio fallisce, è davvero eccezionale.

    
risposta data 06.02.2012 - 17:18
fonte
1

Nel tuo esempio, se la situazione di ValueAlreadyExists può essere facilmente verificata, dovrebbe essere controllata e potrebbe essere sollevata un'eccezione prima di tentare il salvataggio, non penso che una Prova dovrebbe essere necessaria in questo situazione. Le condizioni della gara sono più difficili da controllare in anticipo, quindi avvolgere il salvataggio in una Prova in questo caso è probabilmente un'ottima idea.

In generale, se c'è una condizione che penso sia molto probabile (come NoDataReturned, DivideByZero, ecc ...) O è molto facile da controllare (come una collezione vuota o un valore NULL), provo per controllarlo prima del tempo e affrontarlo prima che io arrivi al punto in cui dovrei prendere un'eccezione. Ammetto che non è sempre facile conoscere queste condizioni prima del tempo, a volte appaiono solo quando il codice è sottoposto a rigorosi test.

    
risposta data 06.02.2012 - 16:47
fonte
0

Il metodo save() deve generare un'eccezione.

Il livello più in alto dovrebbe catturare e informare l'utente , senza terminare il programma, a meno che non sia un programma da riga di comando Unix, nel qual caso è OK terminarlo.

I valori restituiti non sono un buon modo per gestire le eccezioni.

    
risposta data 03.10.2012 - 15:16
fonte

Leggi altre domande sui tag