Saya memanggil, melalui refleksi, metode yang dapat menyebabkan pengecualian. Bagaimana saya bisa meneruskan pengecualian ke pemanggil saya tanpa refleksi pembungkus meletakkan di sekitarnya?
Saya memikirkan kembali InnerException, tetapi ini menghancurkan jejak stack.
Kode contoh:
public void test1()
{
// Throw an exception for testing purposes
throw new ArgumentException("test1");
}
void test2()
{
try
{
MethodInfo mi = typeof(Program).GetMethod("test1");
mi.Invoke(this, null);
}
catch (TargetInvocationException tiex)
{
// Throw the new exception
throw tiex.InnerException;
}
}
Jawaban:
Di .NET 4.5 sekarang ada
ExceptionDispatchInfo
kelasnya.Ini memungkinkan Anda menangkap pengecualian dan melemparkannya kembali tanpa mengubah tumpukan-jejak:
Ini bekerja dengan pengecualian apa pun, bukan hanya
AggregateException
.Itu diperkenalkan karena
await
fitur bahasa C #, yang membuka bungkus pengecualian dalamAggregateException
instance untuk membuat fitur bahasa asinkron lebih seperti fitur bahasa sinkron.sumber
throw;
setelah baris .Throw (), karena kompiler tidak akan tahu itu .Throw () selalu melempar pengecualian.throw;
tidak akan pernah dipanggil sebagai hasilnya, tetapi setidaknya kompiler tidak akan mengeluh jika metode Anda memerlukan objek kembali atau fungsi async.throw;
. Jika Anda menggunakanthrow ex.InnerException;
stack-trace diinisialisasi ulang pada titik itu dilemparkan kembali.ExceptionDispatchInfo.Capture(ex.InnerException ?? ex).Throw();
Hal ini dimungkinkan untuk melestarikan jejak stack sebelum rethrowing tanpa refleksi:
Ini menghabiskan banyak siklus dibandingkan dengan menelepon
InternalPreserveStackTrace
melalui delegasi yang di-cache, tetapi memiliki keuntungan hanya mengandalkan fungsi publik. Berikut adalah beberapa pola penggunaan umum untuk fungsi pelestarian tumpukan-jejak:sumber
InternalPreserveStackTrace
(sekitar 6% lebih lambat dengan 10.000 iterasi). Mengakses ladang secara langsung dengan refleksi adalah sekitar 2,5% lebih cepat daripada memohonInternalPreserveStackTrace
e.Data
kamus dengan string atau kunci objek unik (static readonly object myExceptionDataKey = new object ()
, tapi jangan lakukan ini jika Anda harus membuat serialkan pengecualian di mana saja). Hindari memodifikasie.Message
, karena Anda mungkin memiliki kode di suatu tempat yang mem-parsinge.Message
. Parsinge.Message
itu jahat, tetapi mungkin tidak ada pilihan lain, misalnya jika Anda harus menggunakan perpustakaan pihak ketiga dengan praktik pengecualian yang buruk.Saya pikir taruhan terbaik Anda adalah dengan meletakkan ini di blok tangkapan Anda:
Dan kemudian ekstrak innerexception nanti.
sumber
Tidak ada yang menjelaskan perbedaan antara
ExceptionDispatchInfo.Capture( ex ).Throw()
dan dataranthrow
, jadi ini dia.Cara lengkap untuk mengubah kembali pengecualian yang tertangkap adalah menggunakan
ExceptionDispatchInfo.Capture( ex ).Throw()
(hanya tersedia dari .Net 4.5).Di bawah ini ada kasus yang diperlukan untuk menguji ini:
1.
2.
3.
4.
Kasus 1 dan kasus 2 akan memberi Anda jejak tumpukan di mana nomor baris kode sumber untuk
CallingMethod
metode ini adalah nomor baris darithrow new Exception( "TEST" )
baris tersebut.Namun, kasus 3 akan memberi Anda jejak tumpukan di mana nomor baris kode sumber untuk
CallingMethod
metode ini adalah nomor baristhrow
panggilan. Ini berarti bahwa jikathrow new Exception( "TEST" )
garis dikelilingi oleh operasi lain, Anda tidak tahu di mana nomor garis pengecualian itu sebenarnya dilemparkan.Kasus 4 mirip dengan kasus 2 karena nomor baris pengecualian asli dipertahankan, tetapi bukan rethrow nyata karena mengubah jenis pengecualian asli.
sumber
Panggil metode ekstensi pada pengecualian Anda sebelum Anda melemparkannya, itu akan mempertahankan jejak tumpukan asli.
sumber
Action<Exception>
? Di sini menggunakan metode statisBahkan lebih banyak refleksi ...
Ingatlah bahwa ini dapat pecah kapan saja, karena bidang pribadi bukan bagian dari API. Lihat diskusi lebih lanjut tentang Mono bugzilla .
sumber
InternalPreserveStackTrace
metode internal akan lebih baik, karena melakukan hal yang sama dan kecil kemungkinannya berubah di masa depan ...Pertama: jangan kehilangan TargetInvocationException - ini informasi berharga ketika Anda ingin men-debug sesuatu.
Kedua: Bungkus TIE sebagai InnerException dalam tipe pengecualian Anda sendiri dan letakkan properti OriginalException yang menautkan ke apa yang Anda butuhkan (dan pertahankan seluruh panggilan tetap utuh).
Ketiga: Biarkan gelembung TIE keluar dari metode Anda.
sumber
Kawan, kau keren .. Aku akan segera menjadi ahli nujum.
sumber
.Invoke()
.Kode contoh lain yang menggunakan serialisasi / deserialisasi pengecualian. Itu tidak memerlukan jenis pengecualian aktual menjadi serializable. Juga hanya menggunakan metode publik / dilindungi.
sumber
Berdasarkan jawaban Paul Turners saya membuat metode ekstensi
yang
return ex
ist tidak pernah mencapai tapi keuntungan adalah bahwa saya dapat menggunakanthrow ex.Capture()
sebagai salah satu kapal sehingga compiler tidak akan menaikkannot all code paths return a value
kesalahan.sumber