qual è la differenza tra
try { ... }
catch{ throw }
e
try{ ... }
catch(Exception e) {throw new Exception(e.message) }
Indipendentemente dal fatto che il secondo mostra un messaggio?
qual è la differenza tra
try { ... }
catch{ throw }
e
try{ ... }
catch(Exception e) {throw new Exception(e.message) }
Indipendentemente dal fatto che il secondo mostra un messaggio?
Risposte:
throw; ricodifica l'eccezione originale e conserva la traccia dello stack originale.
throw ex;genera l'eccezione originale ma reimposta la traccia dello stack, distruggendo tutte le informazioni sulla traccia dello stack fino al catchblocco.
throw ex;throw new Exception(ex.Message);è anche peggio. Crea una nuova Exceptionistanza, perdendo la traccia dello stack originale dell'eccezione e il suo tipo. (ad es IOException.).
Inoltre, alcune eccezioni contengono informazioni aggiuntive (ad es ArgumentException.ParamName.).
throw new Exception(ex.Message); distruggerà anche queste informazioni.
In alcuni casi, potresti voler racchiudere tutte le eccezioni in un oggetto eccezione personalizzato, in modo da poter fornire ulteriori informazioni su cosa stava facendo il codice quando è stata generata l'eccezione.
Per fare ciò, definisci una nuova classe che eredita Exception, aggiungi tutti e quattro i costruttori di eccezioni e, facoltativamente, un costruttore aggiuntivo che accetta InnerExceptionsia informazioni aggiuntive sia lancia la tua nuova classe di eccezione, passando excome InnerExceptionparametro . Passando l'originale InnerException, si preservano tutte le proprietà dell'eccezione originale, inclusa la traccia dello stack.
throw new MyCustomException(myMessage, ex);ovvio.
ex.Message, che è peggio.
[Serializable()].
throw;il numero di riga effettivo in cui si è verificata l'eccezione viene sostituito dal numero di riga di throw;. Come suggerisci di gestirlo? stackoverflow.com/questions/2493779/…
Il primo conserva lo stacktrace originale:
try { ... }
catch
{
// Do something.
throw;
}
Il secondo consente di modificare il tipo di eccezione e / o il messaggio e altri dati:
try { ... } catch (Exception e)
{
throw new BarException("Something broke!");
}
C'è anche un terzo modo in cui si passa un'eccezione interna:
try { ... }
catch (FooException e) {
throw new BarException("foo", e);
}
Consiglierei di usare:
Un altro punto che non ho visto nessuno fare:
Se non fai nulla nel blocco catch {}, provare ... catch non ha senso. Lo vedo sempre:
try
{
//Code here
}
catch
{
throw;
}
O peggio:
try
{
//Code here
}
catch(Exception ex)
{
throw ex;
}
Peggio ancora:
try
{
//Code here
}
catch(Exception ex)
{
throw new System.Exception(ex.Message);
}
throwgenera nuovamente l'eccezione rilevata, mantenendo la traccia dello stack, mentre throw new Exceptionperde alcuni dettagli dell'eccezione rilevata.
Normalmente useresti throwda solo per registrare un'eccezione senza gestirla completamente a quel punto.
BlackWasp ha un buon articolo sufficientemente intitolato Throwing Exceptions in C # .
Lanciare una nuova eccezione fa saltare la traccia dello stack corrente.
throw;manterrà la traccia dello stack originale ed è quasi sempre più utile. L'eccezione a quella regola è quando si desidera racchiudere l'eccezione in un'eccezione personalizzata. Dovresti quindi fare:
catch(Exception e)
{
throw new CustomException(customMessage, e);
}
throwè per riproporre un'eccezione rilevata. Questo può essere utile se vuoi fare qualcosa con l'eccezione prima di passarlo nella catena di chiamate.
L'uso throwsenza argomenti preserva lo stack di chiamate a scopo di debug.
Il secondo esempio reimposterà la traccia dello stack dell'eccezione. Il primo conserva in modo più accurato le origini dell'eccezione. Inoltre hai scartato il tipo originale che è la chiave per sapere cosa è effettivamente andato storto ... Se il secondo è richiesto per la funzionalità - ad es. Per aggiungere informazioni estese o riavvolgere con un tipo speciale come un 'HandleableException' personalizzato, basta essere certo che anche la proprietà InnerException è impostata!
La differenza più importante è che la seconda espressione cancella il tipo di eccezione. E il tipo di eccezione svolge un ruolo vitale nel rilevare le eccezioni:
public void MyMethod ()
{
// both can throw IOException
try { foo(); } catch { throw; }
try { bar(); } catch(E) {throw new Exception(E.message); }
}
(...)
try {
MyMethod ();
} catch (IOException ex) {
Console.WriteLine ("Error with I/O"); // [1]
} catch (Exception ex) {
Console.WriteLine ("Other error"); // [2]
}
In caso di foo()lancio IOException, il [1]blocco cattura prenderà un'eccezione. Ma quando bar()lancia IOException, sarà convertito in Exceptionformica normale non sarà catturato dal [1]blocco di cattura.
buttare o lanciare ex, entrambi sono usati per lanciare o riproporre l'eccezione, quando si registra semplicemente le informazioni di errore e non si desidera rinviare alcuna informazione al chiamante, si registra semplicemente l'errore in modalità cattura e si esce. Ma nel caso in cui desideri inviare alcune informazioni significative sull'eccezione al chiamante che usi usa o getta ex. Ora la differenza tra lancio e lancio ex è che il tiro conserva la traccia dello stack e altre informazioni, ma il tiro ex crea un nuovo oggetto eccezione e quindi la traccia dello stack originale viene persa. Quindi, quando dovremmo usare il lancio e il lancio e, ci sono ancora alcune situazioni in cui potresti voler ricominciare un'eccezione come ripristinare le informazioni dello stack di chiamate. Ad esempio, se il metodo si trova in una libreria e si desidera nascondere i dettagli della libreria dal codice chiamante, non si desidera necessariamente che lo stack di chiamate includa informazioni sui metodi privati all'interno della libreria. In tal caso, è possibile rilevare le eccezioni nei metodi pubblici della libreria e quindi ripeterle in modo che lo stack di chiamate inizi con tali metodi pubblici.
Nessuna delle risposte qui mostra la differenza, il che potrebbe essere utile per le persone che lottano per capire la differenza. Considera questo codice di esempio:
using System;
using System.Collections.Generic;
namespace ExceptionDemo
{
class Program
{
static void Main(string[] args)
{
void fail()
{
(null as string).Trim();
}
void bareThrow()
{
try
{
fail();
}
catch (Exception e)
{
throw;
}
}
void rethrow()
{
try
{
fail();
}
catch (Exception e)
{
throw e;
}
}
void innerThrow()
{
try
{
fail();
}
catch (Exception e)
{
throw new Exception("outer", e);
}
}
var cases = new Dictionary<string, Action>()
{
{ "Bare Throw:", bareThrow },
{ "Rethrow", rethrow },
{ "Inner Throw", innerThrow }
};
foreach (var c in cases)
{
Console.WriteLine(c.Key);
Console.WriteLine(new string('-', 40));
try
{
c.Value();
} catch (Exception e)
{
Console.WriteLine(e.ToString());
}
}
}
}
}
Che genera il seguente output:
Bare Throw:
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
at ExceptionDemo.Program.<>c.<Main>g__bareThrow|0_1() in C:\...\ExceptionDemo\Program.cs:line 19
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Rethrow
----------------------------------------
System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<>c.<Main>g__rethrow|0_2() in C:\...\ExceptionDemo\Program.cs:line 35
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Inner Throw
----------------------------------------
System.Exception: outer ---> System.NullReferenceException: Object reference not set to an instance of an object.
at ExceptionDemo.Program.<Main>g__fail|0_0() in C:\...\ExceptionDemo\Program.cs:line 12
at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 43
--- End of inner exception stack trace ---
at ExceptionDemo.Program.<>c.<Main>g__innerThrow|0_3() in C:\...\ExceptionDemo\Program.cs:line 47
at ExceptionDemo.Program.Main(String[] args) in C:\...\ExceptionDemo\Program.cs:line 64
Il lancio nudo, come indicato nelle risposte precedenti, mostra chiaramente sia la riga di codice originale non riuscita (riga 12) sia gli altri due punti attivi nello stack di chiamate quando si è verificata l'eccezione (righe 19 e 64).
L'output del caso re-throw mostra perché è un problema. Quando l'eccezione viene riproposta in questo modo, l'eccezione non includerà le informazioni sullo stack originale. Si noti che throw esono inclusi solo il punto (linea 35) e il punto di stack di chiamate più esterno (linea 64). Sarebbe difficile rintracciare il metodo fail () come fonte del problema se si generano eccezioni in questo modo.
L'ultimo caso (innerThrow) è il più elaborato e include più informazioni di quanto sopra. Poiché stiamo creando un'istanza di una nuova eccezione, abbiamo la possibilità di aggiungere informazioni contestuali (il messaggio "esterno", qui, ma possiamo anche aggiungere al dizionario .Data sulla nuova eccezione) oltre a preservare tutte le informazioni nell'originale eccezione (inclusi collegamenti di aiuto, dizionario dei dati, ecc.).