2009-04-04 5 views
13

Supponiamo che io ho le seguenti due classi in due gruppi differenti:La differenza tra rilanciare la cattura senza parametri e non fare nulla?

//in assembly A 
public class TypeA { 
    // Constructor omitted 
    public void MethodA 
    { 
    try { 
     //do something 
    } 
    catch { 
     throw; 
    } 
    } 
} 
//in assembly B 
public class TypeB { 
    public void MethodB 
    { 
    try { 
     TypeA a = new TypeA(); 
     a.MethodA(); 
    } 
    catch (Exception e) 
     //Handle exception 
    } 
    } 
} 

In questo caso, il try-catch in MethodA solo eleva l'eccezione, ma in realtà non gestirlo. C'è qualche vantaggio nell'usare try-catch in MethodA? In altre parole, c'è una differenza tra questo tipo di blocco try-catch e non l'uso del tutto?

+0

Aiuta il debugging per ottenere il try catch throw ... E si spera che si debbano lanciare a molte eccezioni quando l'applicazione è completa in qualsiasi modo (Il problema delle risorse che è stato menzionato) – bytebender

risposta

7

Nel tuo esempio, non vi è alcun vantaggio. Ma ci sono casi in cui è desiderabile semplicemente schiumare un'eccezione specifica.

public void Foo() 
    { 
     try 
     { 
      // Some service adapter code 

      // A call to the service 
     } 
     catch (ServiceBoundaryException) 
     { 
      throw; 
     } 
     catch (Exception ex) 
     { 
      throw new AdapterBoundaryException("some message", ex); 
     } 
    } 

Ciò consente di identificare facilmente che contorno al un'eccezione si è verificato in. In questo caso, si avrebbe bisogno per garantire la vostra eccezioni al contorno sono gettati solo per codice specifico per il contorno.

+0

... a meno che il codice non si trovi nel percorso hot o sia profondamente ricorsivo (quindi l'eccezione viene ripetutamente ripetuta), vedere http://blogs.msdn.com/curth/archive/2008/07/29/stackoverflowexception- e-ironpython.aspx –

2

Con il codice nel modo in cui lo hai scritto per MethodA, non c'è differenza. Tutto ciò che farà è mangiare i cicli del processore. Tuttavia, può esserci un vantaggio nella scrittura del codice in questo modo se c'è una risorsa che è necessario liberare. Ad esempio

Resource r = GetSomeResource(); 
try { 
    // Do Something 
} catch { 
    FreeSomeResource(); 
    throw; 
} 
FreeSomeResource(); 

Tuttavia, non esiste un vero punto nel farlo in questo modo. Sarebbe molto meglio per utilizzare invece un blocco finally.

+0

Meglio usare il blocco finally con una variabile di bandiera booleana, brutta com'è. CIL ha effettivamente gestori di errori che fanno esattamente questo, ma per qualche motivo sconosciuto C# non li espone. –

+0

@Anton, hah, ho letto questo come una domanda in C++ :) – JaredPar

+0

Questa tecnica è utile se la funzione stava progettando di restituire la risorsa. L'eccezione annulla tale piano, quindi la funzione dovrebbe ripulire la risorsa stessa prima di consentire la propagazione dell'eccezione. –

1

Il ripensamento non ha senso - è lo stesso che se non facessi nulla.

Tuttavia risulta utile quando si esegue effettivamente qualcosa: la cosa più comune è registrare l'eccezione. Puoi anche cambiare lo stato della tua classe, qualunque sia.

3

Sì, c'è una differenza. Quando si rileva un'eccezione, .NET presume che lo si gestirà in qualche modo, lo stack è svolto fino alla funzione che sta facendo il catch.

Se non lo si cattura, si verificherà un'eccezione non gestita, che invocherà una sorta di diagnostica (come un debugger o un logger di eccezioni), lo stack completo e il relativo stato nel punto di errore effettivo saranno disponibile per ispezione.

Quindi, se rilevi un'eccezione che non viene gestita altrove, rubi lo strumento diagnostico delle informazioni veramente utili su ciò che è realmente accaduto.

+0

Hm, questo è sottile.Quando si ripresenta un'eccezione diversa, la traccia dello stack viene persa a meno che non la si salvi esplicitamente, ma se si solleva nuovamente l'eccezione originale ('throw;'), la traccia dello stack viene conservata. –

+0

Anthony, penso che ti sbagli. http://blog.dotnetclr.com/archive/2007/11/05/Re-throwing-an-exception.aspx –

+0

OK, la mia formulazione non era abbastanza precisa: 'buttare ex;' e 'throw;' * sono * Diverso, ed entrambi si potrebbe dire di "ripensare l'eccezione originale", ma ho scritto 'throw ;, e il tuo link è d'accordo con me (giusto prima della fine del post collegato). –

1

Mai fare l'opzione A. Come dice Anton, mangia la traccia dello stack. L'esempio di JaredPar mangia anche lo stacktrace. Una soluzione migliore sarebbe:

SomeType* pValue = GetValue(); 
try { 
    // Do Something 
} finally { 
    delete pValue; 
} 

Se hai qualcosa in C# che deve essere rilasciato, ad esempio, un FileStream avete ottenuto le seguenti due scelte:

FileStream stream; 
try 
{ 
    stream = new FileStream("C:\\afile.txt"); 
    // do something with the stream 
} 
finally 
{ 
    // Will always close the stream, even if there are an exception 
    stream.Close(); 
} 

O in modo più pulito:

using (FileStream stream = new FileStream("c:\\afile.txt")) 
{ 
    // do something with the stream 
} 

L'istruzione using eliminerà (e chiuderà) lo stream una volta terminato o quando viene chiusa un'eccezione.

+0

Stavo parlando dello stack space, non dello stack trace. La traccia di stack è OK nel caso particolare di rilanciare l'eccezione originale. –

+2

Il codice di Jared non distrugge neanche la traccia dello stack. La traccia dello stack mostrerà ancora la fonte originale dell'eccezione. Stai confondendolo con l'individuazione di un'eccezione con un parametro e lanciando nuovamente l'eccezione rilevata: "catch (Exception e) {throw e;}" - che traspone la traccia dello stack. –

1

Quando si riprende e si lancia, consente di impostare un punto di interruzione sulla linea throw.

+0

Il debugger può rompere su un excception. Non farlo solo per i breakpoint. È probabile che il codice venga spedito in questo modo. –

+0

L'interruzione dell'eccezione non equivale all'interruzione della procedura in cui si trova il punto di interruzione. Inoltre, la domanda non chiedeva se fosse una buona idea. Ha chiesto quale fosse l'effetto. –

+0

@Rob: sono d'accordo con John, questa non è una buona idea. – AnthonyWJones

1

Le eccezioni di rilancio possono essere utilizzate per incapsularlo in un'eccezione generica come ... considerare l'esempio seguente.

public class XmlException: Exception{ 
    .... 
} 

public class XmlParser{ 
    public void Parse() 
    { 
     try{ 
      .... 
     } 
     catch(IOException ex) 
     { 
     throw new XmlException("IO Error while Parsing", ex); 
     } 
    } 
} 

Ciò offre vantaggi rispetto alla categorizzazione delle eccezioni. Questo è il modo in cui i gestori di file aspx e molti altri codici di sistema eseguono l'incapsulamento delle eccezioni che determina il loro percorso verso lo stack e il loro flusso di logica.

+0

La domanda a cui hai risposto non è quella che ha chiesto Cullen. –

3

Preso così com'è, la prima opzione sembrerebbe una brutta idea (o dovrebbe essere "inutile"?). Tuttavia, è raramente fatto in questo modo. Le eccezioni vengono rigettate da un blocco Catch in genere in due condizioni:

a. Si desidera controllare l'eccezione generata per i dati e condizionarla con bolle condizionali nello stack.

try 
{ 
    //do something 
} 
catch (Exception ex) 
{ 
    //Check ex for certain conditions. 
    if (ex.Message = "Something bad") 
    throw ex; 
    else 
    //Handle the exception here itself. 
} 

b. Si è verificata una condizione inaccettabile all'interno di un componente e tali informazioni devono essere comunicate al codice chiamante (in genere aggiungendo altre informazioni utili o includendole in un altro tipo di eccezione).

try 
{ 
    //do something 
} 
catch (StackOverflowException ex) 
{ 
    //Bubble up the exception to calling code 
    //by wrapping it up in a custom exception. 
    throw new MyEuphemisticException(ex, "Something not-so-good just happened!"); 
} 
+1

"buttare ex" è sbagliato - è un errore nello stack. Basta fare "lanciare" –

+0

Riesci davvero a catturare StackOverflowException? Pensavo che .NET 2.0+ non ti permettesse di farlo. –

+2

Si prega di non utilizzare la proprietà message di un'eccezione per la logica condizionale in quanto possono essere localizzati. –

1

L'assemblea A - try catch-block non ha alcun senso per me. Credo che se non gestirai l'eccezione, allora perché stai prendendo quelle eccezioni. Sarebbe comunque proiettato al livello successivo.

Tuttavia, se si sta creando un'API di livello intermedio o qualcosa del genere e la gestione di un'eccezione (e quindi di un'eccezione) in quel livello non ha senso, è possibile lanciare il proprio strato ApplicationException. Ma certamente rilanciare la stessa eccezione non ha senso.

1

Poiché le classi si trovano in 2 diversi gruppi, è possibile che si desideri o semplicemente catturare l'eccezione per registrarlo e quindi restituirlo al chiamante, in modo che possa gestirlo nel modo che ritiene opportuno. Un lancio invece di un lancio ex conserverà le informazioni contestuali su dove ha origine l'eccezione. Questo può rivelarsi utile quando l'assembly è un'API/framework in cui non devi mai ingoiare le eccezioni a meno che non sia significativo per farlo, ma comunque utile nella risoluzione dei problemi se è loggato ad esempio su EventLog.

0

È possibile utilizzare il blocco try {} catch (ex) {} nel metodo A solo se è possibile rilevare l'eccezione specifica che può essere gestita in MethodA() (ad esempio: registrazione).

Un'altra opzione è concatenare l'eccezione utilizzando la proprietà InnerException e passarla al chiamante. Questa idea non uccide la traccia dello stack.