Perché i blocchi di cattura vuoti sono una cattiva idea? [chiuso]


194

Ho appena visto una domanda su try-catch , che le persone (incluso Jon Skeet) dicono che i blocchi di cattura vuoti sono davvero una cattiva idea? Perchè questo? Non esiste una situazione in cui una presa vuota non è una decisione di progettazione sbagliata?

Voglio dire, ad esempio, a volte vuoi ottenere alcune informazioni aggiuntive da qualche parte (servizio web, database) e davvero non ti importa se otterrai queste informazioni o meno. Quindi provi a ottenerlo, e se succede qualcosa, va bene, aggiungerò solo un "catch (eccezione ignorata) {}" e questo è tutto


53
Scriverei un commento spiegando perché dovrebbe essere vuoto.
Mehrdad Afshari,

5
... o almeno registra che è stato catturato.
matt b

2
@ocdecio: evitarlo per il codice di pulizia , non evitarlo in generale.
John Saunders,

1
@ocdecio: un altro caso per evitare di usare try..catch è quando si rilevano eccezioni per tipi noti di errori. ad es. eccezioni di conversione numerica
MPritchard

1
@ocdecio - try..finally è meglio di try..empty catch perché l'errore continua nello stack - per essere gestito dal framework o per uccidere il programma ed essere mostrato all'utente - entrambi i risultati sono migliori di un " fallimento silenzioso ".
Nate,

Risposte:


298

Di solito un tentativo di cattura vuoto è una cattiva idea perché stai ingoiando silenziosamente una condizione di errore e poi continuando l'esecuzione. Occasionalmente questa potrebbe essere la cosa giusta da fare, ma spesso è un segno che uno sviluppatore ha visto un'eccezione, non sapeva cosa fare al riguardo e quindi ha usato una cattura vuota per mettere a tacere il problema.

È l'equivalente di programmazione del mettere del nastro nero sopra una spia del motore.

Credo che il modo in cui gestisci le eccezioni dipende dal livello del software in cui stai lavorando: Eccezioni nella foresta pluviale .


4
In quelle circostanze davvero rare in cui non ho davvero bisogno dell'eccezione e la registrazione non è disponibile per qualche motivo, mi assicuro di commentare che è intenzionale - e perché non è necessario, e ribadisco che preferirei comunque registrarlo se era fattibile in quell'ambiente. Fondamentalmente faccio il commento PIÙ lavoro rispetto alla registrazione sarebbe stata se fosse stata un'opzione in quella circostanza. Fortunatamente ho solo 2 clienti per i quali questo è un problema, ed è solo quando si inviano e-mail non critiche dai loro siti web.
John Rudy,

37
Adoro anche l'analogia - e come tutte le regole, ci sono eccezioni. Ad esempio, è perfettamente OK utilizzare il nastro nero sull'orologio lampeggiante sul videoregistratore se non si intende mai fare registrazioni a tempo (per tutti i vecchi che ricordano cos'è un videoregistratore).
Michael Burr,

3
Come ogni allontanamento dalla pratica accettata, fallo se necessario e documentalo in modo che il prossimo ragazzo sappia cosa hai fatto e perché.
David Thornley,

5
@Jason: per lo meno, dovresti includere un commento dettagliato che spieghi perché stai zittendo le eccezioni.
Ned Batchelder,

1
Questa risposta presuppone due cose: 1. Che l'eccezione lanciata sia davvero significativa e che chi ha scritto il codice che lancia sapesse cosa stava facendo; 2. Che l'eccezione sia effettivamente una "condizione di errore" e non venga abusata come flusso di controllo (sebbene questo sia un caso più specifico del mio primo punto).
Boris B.

38

Sono una cattiva idea in generale perché è una condizione davvero rara in cui un fallimento (condizione eccezionale, più genericamente) viene correttamente incontrato senza alcuna risposta. Inoltre, i catchblocchi vuoti sono uno strumento comune utilizzato dalle persone che utilizzano il motore delle eccezioni per controllare che debbano essere eseguite preventivamente.

Dire che è sempre un male non è vero ... questo è vero per ben poco. Ci possono essere circostanze in cui o non ti interessa che si sia verificato un errore o che la presenza dell'errore indichi in qualche modo che non puoi fare nulla al riguardo (ad esempio, quando scrivi un errore precedente in un file di registro di testo e si ottiene un IOException, il che significa che non è possibile scrivere il nuovo errore comunque).


11

Non allungherei le cose fino a dire che chi usa blocchi di cattura vuoti è un cattivo programmatore e non sa cosa sta facendo ...

Uso blocchi di cattura vuoti se necessario. A volte il programmatore della biblioteca che sto consumando non sa cosa sta facendo e genera eccezioni anche in situazioni in cui nessuno ne ha bisogno.

Ad esempio, considera alcune librerie del server http, non mi potrebbe importare di meno se il server genera un'eccezione perché il client si è disconnesso e index.htmlnon è stato possibile inviarlo.


6
Stai certamente generalizzando troppo. Solo perché non ne hai bisogno non significa che nessuno lo faccia. Anche se non si può assolutamente fare nulla in risposta, c'è qualcuno da qualche parte con l'obbligo di raccogliere statistiche sulle connessioni abbandonate. Quindi è abbastanza scortese presumere che "il programmatore della biblioteca non sappia cosa sta facendo".
Ben Voigt,

8

Ci sono rari casi in cui può essere giustificato. In Python vedi spesso questo tipo di costruzione:

try:
    result = foo()
except ValueError:
    result = None

Quindi potrebbe essere OK (a seconda dell'applicazione) fare:

result = bar()
if result == None:
    try:
        result = foo()
    except ValueError:
        pass # Python pass is equivalent to { } in curly-brace languages
 # Now result == None if bar() returned None *and* foo() failed

In un recente progetto .NET, ho dovuto scrivere codice per enumerare le DLL dei plug-in per trovare le classi che implementano una determinata interfaccia. Il bit di codice rilevante (in VB.NET, scusa) è:

    For Each dllFile As String In dllFiles
        Try
            ' Try to load the DLL as a .NET Assembly
            Dim dll As Assembly = Assembly.LoadFile(dllFile)
            ' Loop through the classes in the DLL
            For Each cls As Type In dll.GetExportedTypes()
                ' Does this class implement the interface?
                If interfaceType.IsAssignableFrom(cls) Then

                    ' ... more code here ...

                End If
            Next
        Catch ex As Exception
            ' Unable to load the Assembly or enumerate types -- just ignore
        End Try
    Next

Anche se anche in questo caso, ammetterei che la registrazione dell'errore da qualche parte sarebbe probabilmente un miglioramento.


Ho fatto riferimento a log4net come una di quelle istanze prima di vedere la tua risposta.
Scott Lawrence,

7

I blocchi catch vuoti vengono generalmente inseriti perché il programmatore non sa davvero cosa stanno facendo. Nella mia organizzazione, un blocco di cattura vuoto deve includere un commento sul perché non fare nulla con l'eccezione è una buona idea.

In una nota correlata, la maggior parte delle persone non sa che un blocco try {} può essere seguito con una cattura {} o infine {}, ne è richiesto solo uno.


1
+1 Sottolineerei il 'o' in quest'ultima frase per un maggiore impatto
MPritchard

Quel secondo paragrafo può dipendere dalla lingua, giusto?
David Z,

3
a meno che, naturalmente, non si stia parlando di IE6 o IE7 ;-) ... in cui è richiesta la cattura o che infine non viene eseguita. (risolto in IE8 tra l'altro)
scunliffe,


Verissimo. Potrebbe valere la pena evidenziare le lingue. Credo che .net
vada

7

Le eccezioni dovrebbero essere lanciate solo se esiste davvero un'eccezione - qualcosa che accade oltre la norma. Un blocco di cattura vuoto dice sostanzialmente "sta succedendo qualcosa di brutto, ma non mi interessa". Questa è una cattiva idea.

Se non si desidera gestire l'eccezione, lasciarla propagare verso l'alto fino a raggiungere un codice in grado di gestirlo. Se nulla può gestire l'eccezione, l'applicazione dovrebbe essere disattivata.


3
A volte sai che non influenzerà nulla. Quindi vai avanti e fallo, e commentalo in modo che il prossimo non pensi solo che hai fatto un casino perché sei incompetente.
David Thornley,

Sì, c'è un tempo e un posto per tutto. In generale, tuttavia, penso che un blocco catch vuoto che sia buono sia l'eccezione alla regola - è un caso raro in cui si vuole veramente ignorare un'eccezione (di solito, IMO, è il risultato di un cattivo uso delle eccezioni).
Reed Copsey,

2
@ David Thornley: come fai a sapere che non influirà su nulla se non ispezionerai almeno l'eccezione per determinare se si tratta di un errore atteso e insignificante o che dovrebbe invece essere propagato verso l'alto?
Dave Sherohman,

2
@Dave: anche se catch (Exception) {}è una cattiva idea, catch (SpecificExceptionType) {}potrebbe andare benissimo. Il programmatore DID ha ispezionato l'eccezione, utilizzando le informazioni sul tipo nella clausola catch.
Ben Voigt,

7

Penso che vada bene se catturi un particolare tipo di eccezione di cui sai che verrà sollevato solo per un motivo particolare e ti aspetti che l'eccezione e davvero non debba fare nulla al riguardo.

Ma anche in quel caso, un messaggio di debug potrebbe essere in ordine.


6

Per Josh Bloch - Item 65: Non ignorare le eccezioni di Java efficace :

  1. Un blocco di cattura vuoto vanifica lo scopo delle eccezioni
  2. Per lo meno, il blocco catch dovrebbe contenere un commento che spiega perché è opportuno ignorare l'eccezione.

3

Un blocco di cattura vuoto sta essenzialmente dicendo "Non voglio sapere quali errori vengono generati, li ignorerò".

È simile ai VB6 On Error Resume Next, tranne per il fatto che qualsiasi cosa nel blocco try dopo che viene generata l'eccezione verrà ignorata.

Il che non aiuta quando qualcosa si rompe.


In realtà direi che "On Error Resume Next" è peggio - proverà semplicemente la riga successiva a prescindere. Una cattura vuota salterà per superare la parentesi graffa di chiusura
dell'istruzione

Buon punto. Dovrei probabilmente notare questo nella mia risposta.
Powerlord,

1
Questo non è esattamente vero, se hai un tentativo vuoto ... cattura con un tipo di eccezione specifico, ignorerà solo questo tipo di eccezione e potrebbe essere legittimo ...
Meta-Knight

Ma se sai che viene lanciato un particolare tipo di eccezione, sembra che potresti avere abbastanza informazioni per scrivere la tua logica in un modo che eviti l'uso di try-catch per affrontare la situazione.
Scott Lawrence,

@Scott: Alcune lingue (ad es. Java) hanno verificato le eccezioni ... eccezioni per cui sei costretto a scrivere blocchi di cattura.
Powerlord,

3

Questo va di pari passo con "Non usare le eccezioni per controllare il flusso del programma" e "Usa le eccezioni solo per circostanze eccezionali". In questo caso, si dovrebbero verificare eccezioni solo in caso di problemi. E se c'è un problema, non vuoi fallire silenziosamente. Nelle rare anomalie in cui non è necessario gestire il problema, è necessario registrare almeno l'eccezione, nel caso in cui l'anomalia non diventi più un'anomalia. L'unica cosa peggiore del fallimento è il fallimento silenzioso.


2

Penso che un blocco catch completamente vuoto sia una cattiva idea perché non c'è modo di dedurre che ignorare l'eccezione fosse il comportamento previsto del codice. Non è necessariamente male ingoiare un'eccezione e restituire false o null o qualche altro valore in alcuni casi. Il framework .net ha molti metodi "provare" che si comportano in questo modo. Come regola generale se si ingoia un'eccezione, aggiungere un commento e un'istruzione di registro se l'applicazione supporta la registrazione.


1

Perché se viene generata un'eccezione non la vedrai mai - fallire in silenzio è l'opzione peggiore possibile - otterrai un comportamento errato e nessuna idea di guardare dove sta accadendo. Almeno metti un messaggio di log lì! Anche se è qualcosa che "non può mai accadere"!


1

I blocchi di cattura vuoti indicano che un programmatore non sa cosa fare con un'eccezione. Stanno sopprimendo l'eccezione dal possibile gorgogliamento e dalla corretta gestione da parte di un altro blocco try. Cerca sempre di fare qualcosa con l'eccezione che stai rilevando.


1

Trovo che il più fastidioso con le dichiarazioni di cattura vuote sia quando lo ha fatto qualche altro programmatore. Ciò che intendo è quando è necessario eseguire il debug del codice da qualcun altro qualsiasi dichiarazione di cattura vuota rende un'impresa del genere più difficile di quanto debba essere. Le dichiarazioni catch IMHO dovrebbero sempre mostrare un qualche tipo di messaggio di errore - anche se l'errore non viene gestito dovrebbe almeno rilevarlo (alt. Attivo solo in modalità debug)


1

Probabilmente non è mai la cosa giusta perché stai silenziosamente passando ogni possibile eccezione. Se ci si aspetta un'eccezione specifica, è necessario verificarla, riprovare se non è l'eccezione.

try
{
    // Do some processing.
}
catch (FileNotFound fnf)
{
    HandleFileNotFound(fnf);
}
catch (Exception e)
{
    if (!IsGenericButExpected(e))
        throw;
}

public bool IsGenericButExpected(Exception exception)
{
    var expected = false;
    if (exception.Message == "some expected message")
    {
        // Handle gracefully ... ie. log or something.
        expected = true;
    }

    return expected;
}

1
Non è proprio uno schema standard che stai consigliando alle persone di usare lì. Molte persone catturerebbero solo le eccezioni di qualunque tipo si aspettino e non quelle che non sono. Vedo alcune circostanze in cui il tuo approccio sarebbe valido, stai solo attento a pubblicare in un forum come questo in cui le persone tendono a leggere cose come questa perché non capiscono in primo luogo;)
MPritchard

Il mio intento non era che IsKnownException () stesse controllando il tipo di eccezione (sì, dovresti farlo con blocchi catch diversi), piuttosto sta controllando se si tratta di un'eccezione prevista. Modificherò per renderlo più chiaro.
xanadont,

Inizialmente stavo pensando a COMExceptions. È possibile verificare un ErrorCode specifico e gestire i codici previsti. Lo faccio spesso durante la programmazione con ArcOjbects.
xanadont,

2
-1 Prendere decisioni nel codice in base al messaggio di eccezione è una pessima idea. Il messaggio esatto è raramente documentato e potrebbe cambiare in qualsiasi momento; è un dettaglio di implementazione. Oppure il messaggio potrebbe essere basato su una stringa di risorse localizzabile. In ogni caso è pensato solo per essere letto da un essere umano.
Wim Coenen,

Eek. L'eccezione Il messaggio potrebbe essere localizzato e quindi non quello che ti aspetti. Questo è semplicemente sbagliato.
Frankhommers,

1

In generale, dovresti solo cogliere le eccezioni che puoi effettivamente gestire. Ciò significa essere il più specifici possibile quando si rilevano eccezioni. Catturare tutte le eccezioni è raramente una buona idea e ignorare tutte le eccezioni è quasi sempre una pessima idea.

Posso solo pensare ad alcuni casi in cui un blocco catch vuoto ha uno scopo significativo. Se qualunque specifica eccezione, si sta rilevando, viene "gestita" semplicemente tentando nuovamente di agire, non sarebbe necessario fare nulla nel blocco catch. Tuttavia, sarebbe comunque una buona idea registrare il fatto che si è verificata l'eccezione.

Un altro esempio: CLR 2.0 ha cambiato il modo in cui vengono trattate le eccezioni non gestite sul thread del finalizzatore. Prima della 2.0 il processo era autorizzato a sopravvivere a questo scenario. Nell'attuale CLR il processo viene terminato in caso di un'eccezione non gestita sul thread del finalizzatore.

Tieni presente che dovresti implementare un finalizzatore solo se ne hai davvero bisogno e anche in questo caso dovresti fare il meno possibile nel finalizzatore. Ma se qualunque lavoro il tuo finalizzatore debba fare potrebbe generare un'eccezione, devi scegliere tra il minore di due mali. Vuoi chiudere l'applicazione a causa dell'eccezione non gestita? O vuoi procedere in uno stato più o meno indefinito? Almeno in teoria quest'ultimo può essere il minore dei due mali in alcuni casi. In questi casi, il blocco di cattura vuoto impedirebbe di terminare il processo.


1
Voglio dire, ad esempio, a volte vuoi ottenere alcune informazioni aggiuntive da qualche parte (servizio web, database) e davvero non ti importa se otterrai queste informazioni o meno. Quindi provi a ottenerlo, e se succede qualcosa, va bene, aggiungerò solo un "catch (eccezione ignorata) {}" e questo è tutto

Quindi, con il tuo esempio, è una cattiva idea in quel caso perché stai catturando e ignorando tutte le eccezioni. Se prendessi solo EInfoFromIrrelevantSourceNotAvailablee lo ignorassi, andrebbe bene, ma non lo sei. Stai anche ignorando ENetworkIsDown, il che può essere o non essere importante. Stai ignorando ENetworkCardHasMeltede EFPUHasDecidedThatOnePlusOneIsSeventeen, che sono quasi certamente importanti.

Un blocco di cattura vuoto non è un problema se è impostato per catturare (e ignorare) solo eccezioni di determinati tipi che non sono importanti. Le situazioni in cui è una buona idea sopprimere e ignorare silenziosamente tutte le eccezioni, senza smettere di esaminarle prima per vedere se sono attese / normali / irrilevanti o meno, sono estremamente rare.


1

Ci sono situazioni in cui potresti usarle, ma dovrebbero essere molto rare. Le situazioni in cui potrei usarne una includono:

  • registrazione delle eccezioni; a seconda del contesto potresti invece voler pubblicare un'eccezione non gestita o un messaggio.

  • situazioni tecniche in loop, come il rendering o l'elaborazione del suono o una richiamata della casella di riepilogo, in cui il comportamento stesso dimostrerà il problema, il lancio di un'eccezione si metterà in mezzo e la registrazione dell'eccezione probabilmente causerà migliaia di messaggi "non riusciti a XXX" .

  • programmi che non possono fallire, anche se dovrebbero comunque registrare qualcosa.

per la maggior parte delle applicazioni winforms, ho scoperto che è sufficiente avere una singola istruzione try per ogni input dell'utente. Uso i seguenti metodi: (AlertBox è solo un veloce wrapper MessageBox.Show)

  public static bool TryAction(Action pAction)
  {
     try { pAction(); return true; }
     catch (Exception exception)
     {
        LogException(exception);
        return false;
     }
  }

  public static bool TryActionQuietly(Action pAction)
  {
     try { pAction(); return true; }
     catch(Exception exception)
     {
        LogExceptionQuietly(exception);
        return false;
     }
  }

  public static void LogException(Exception pException)
  {
     try
     {
        AlertBox(pException, true);
        LogExceptionQuietly(pException);
     }
     catch { }
  }

  public static void LogExceptionQuietly(Exception pException)
  {
     try { Debug.WriteLine("Exception: {0}", pException.Message); } catch { }
  }

Quindi ogni gestore di eventi può fare qualcosa del tipo:

  private void mCloseToolStripMenuItem_Click(object pSender, EventArgs pEventArgs)
  {
     EditorDefines.TryAction(Dispose);
  }

o

  private void MainForm_Paint(object pSender, PaintEventArgs pEventArgs)
  {
     EditorDefines.TryActionQuietly(() => Render(pEventArgs));
  }

Teoricamente, potresti avere TryActionSilently, che potrebbe essere migliore per il rendering delle chiamate in modo che un'eccezione non generi una quantità infinita di messaggi.


1

Se non sai cosa fare nel blocco catch, puoi semplicemente registrare questa eccezione, ma non lasciarla vuota.

        try
        {
            string a = "125";
            int b = int.Parse(a);
        }
        catch (Exception ex)
        {
            Log.LogError(ex);
        }

Perché questo è stato downvoted?
Vino,

0

Non dovresti mai avere un blocco di cattura vuoto. È come nascondere un errore che conosci. Per lo meno, è necessario scrivere un'eccezione a un file di registro per rivederlo in seguito se si preme per un po 'di tempo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.