Akankah Try / Akhirnya (tanpa Catch) menjadi pengecualian?

115

Saya hampir yakin jawabannya adalah YA. Jika saya menggunakan blok Try Akhirnya tetapi tidak menggunakan blok Catch maka pengecualian apa pun AKAN muncul. Benar?

Ada pemikiran tentang latihan secara umum?

Seth

Seth Spearman
sumber

Jawaban:

130

Ya, itu pasti akan terjadi. Dengan asumsi finallyblok Anda tidak memunculkan pengecualian, tentu saja, dalam hal ini hal itu akan secara efektif "menggantikan" yang semula dilemparkan.

Jon Skeet
sumber
13
@David: Anda tidak dapat kembali dari blok terakhir di C #.
Jon Skeet
3
Dokumentasi msdn juga mengkonfirmasi jawaban ini: Alternatifnya, Anda dapat menangkap pengecualian yang mungkin dilemparkan di blok try dari pernyataan coba-akhirnya yang lebih tinggi dari tumpukan panggilan. Artinya, Anda bisa menangkap pengecualian dalam metode yang memanggil metode yang berisi pernyataan coba-akhirnya, atau dalam metode yang memanggil metode itu, atau dalam metode apa pun dalam tumpukan panggilan. Jika pengecualian tidak tertangkap, eksekusi blok akhirnya bergantung pada apakah sistem operasi memilih untuk memicu operasi pelepasan pengecualian.
broadband
60

Ada pemikiran tentang latihan secara umum?

Iya. Berhati-hatilah . Saat pemblokiran terakhir Anda berjalan, sangat mungkin blok tersebut berjalan karena pengecualian yang tidak tertangani dan tidak terduga telah dilemparkan . Itu berarti ada sesuatu yang rusak , dan sesuatu yang sama sekali tidak terduga bisa terjadi.

Dalam situasi itu, Anda tidak boleh menjalankan kode pada blok terakhir sama sekali. Kode pada blok terakhir dapat dibangun untuk mengasumsikan bahwa subsistem yang bergantung padanya sehat, padahal sebenarnya mereka bisa sangat rusak. Kode di blok terakhir bisa memperburuk keadaan.

Misalnya, saya sering melihat hal semacam ini:

DisableAccessToTheResource();
try
{
    DoSomethingToTheResource();
}
finally
{
    EnableAccessToTheResource();
}

Penulis kode ini berpikir, "Saya membuat mutasi sementara pada keadaan dunia; saya perlu mengembalikan keadaan ke keadaan sebelum saya dipanggil". Tapi mari kita pikirkan tentang semua kemungkinan kesalahan ini.

Pertama, akses ke sumber daya sudah bisa dinonaktifkan oleh pemanggil; dalam hal ini, kode ini mengaktifkannya kembali, mungkin sebelum waktunya.

Kedua, jika DoSomethingToTheResource melontarkan pengecualian, apakah hal yang benar untuk dilakukan untuk mengaktifkan akses ke sumber daya ??? Kode yang mengelola sumber daya tiba - tiba rusak . Kode ini mengatakan, pada dasarnya "jika kode manajemen rusak, pastikan bahwa kode lain dapat memanggil kode yang rusak itu secepat mungkin, sehingga bisa juga gagal total ." Sepertinya ini ide yang buruk.

Ketiga, jika DoSomethingToTheResource melontarkan pengecualian, lalu bagaimana kita tahu bahwa EnableAccessToTheResource juga tidak akan menampilkan pengecualian? Apa pun kerusakan yang terjadi, penggunaan sumber daya juga dapat memengaruhi kode pembersihan, dalam hal ini pengecualian asli akan hilang dan masalah akan lebih sulit didiagnosis.

Saya cenderung menulis kode seperti ini tanpa menggunakan blok try-akhirnya:

bool wasDisabled = IsAccessDisabled();
if (!wasDisabled)
    DisableAccessToTheResource();
DoSomethingToTheResource();
if (!wasDisabled)
    EnableAccessToTheResource();

Sekarang status tidak bermutasi kecuali perlu. Sekarang status penelepon tidak dipusingkan. Dan sekarang, jika DoSomethingToTheResource gagal, maka kami tidak mengaktifkan kembali akses. Kami berasumsi bahwa ada sesuatu yang rusak parah dan tidak mengambil risiko memperburuk situasi dengan mencoba terus menjalankan kode. Biarkan penelepon mengatasi masalahnya, jika mereka bisa.

Jadi, kapan ide yang bagus untuk menjalankan blok terakhir? Pertama, saat pengecualian diharapkan. Misalnya, Anda mungkin berharap bahwa upaya untuk mengunci file mungkin gagal, karena orang lain telah menguncinya. Dalam kasus ini, masuk akal untuk menangkap pengecualian dan melaporkannya kepada pengguna. Dalam hal ini, ketidakpastian tentang apa yang rusak berkurang; Anda tidak mungkin memperburuk keadaan dengan membersihkan.

Kedua, ketika sumber daya yang Anda bersihkan adalah sumber daya sistem yang langka. Misalnya, masuk akal untuk menutup pegangan file di blok akhirnya. (A "menggunakan" tentu saja hanyalah cara lain untuk menulis blok coba-akhirnya.) Konten file mungkin rusak, tetapi tidak ada yang dapat Anda lakukan sekarang. Pegangan file akan ditutup pada akhirnya, jadi mungkin juga lebih cepat daripada nanti.

Eric Lippert
sumber
7
Lebih banyak lagi contoh bagaimana kita benar-benar belum menyatukan tindakan kita sebagai sebuah industri dalam hal penanganan kesalahan. Bukannya saya memiliki saran yang lebih baik daripada pengecualian, tetapi saya berharap masa depan memiliki sesuatu yang lebih mungkin untuk mengarahkan pada tindakan yang benar diambil dengan mudah.
Jon Skeet
Di vb.net, mungkin kode di blok Akhirnya mengetahui pengecualian apa yang tertunda. Jika seseorang menyiapkan hierarki pengecualian untuk membedakan "tidak melakukannya; sebutkan jika tidak oke" dari "status rusak", Anda dapat menjalankan blok Akhirnya hanya jika pengecualian yang tertunda bukan salah satu yang buruk. Saya suka memiliki rutinitas pembuangan / pembersihan menangkap pengecualian dan melempar DisposerFailedException (yang termasuk dalam kategori "buruk", dan menyertakan pengecualian asli sebagai InnerException). Saya ingin melihat antarmuka iDisposableEx standar dengan Dispose (Ex as Exception) untuk memfasilitasi itu.
supercat
Meskipun saya menghargai bahwa ada kasus di mana pengecualian dapat terjadi saat suatu objek dalam keadaan buruk dan upaya pembersihan akan memperburuk keadaan, saya pikir masalah seperti itu harus ditangani dalam kode pembersihan, mungkin dengan bantuan delegasi. Pembungkus kunci, misalnya, dapat mengekspos delegasi fungsi "CheckIfSafeToUnlock (Ex as Exception)" yang dapat disetel pada saat objek terkunci dalam status tidak valid dan dihapus jika tidak. Sebelum membuka kunci, pembungkus dapat memeriksa delegasi; jika tidak kosong, itu bisa menjalankannya dan melepaskan kunci hanya jika mengembalikan true.
supercat
Memiliki pembungkus yang menggunakan delegasi seperti itu akan memungkinkan kode yang memanipulasi status objek untuk memilih tindakan pembersihan yang sesuai jika terganggu. Tindakan tersebut mungkin termasuk memperbaiki struktur data dan mengembalikan True, melemparkan pengecualian "lebih parah" lainnya yang membungkus yang asli, atau membiarkan pengecualian asli meresap saat mengembalikan False.
supercat
2
Eric, terima kasih atas jawaban Anda yang luar biasa. Saya kira saya harus mengajukan dua pertanyaan karena Anda dan Jon benar. Bagaimanapun, Anda mendapat suara positif. Terima kasih atas perhatian Anda atas pertanyaan tersebut.
Seth Spearman