Saya tidak punya masalah; Saya hanya penasaran. Bayangkan skenario berikut:
foreach (var foo in list)
{
try
{
//Some code
}
catch (Exception)
{
//Some more code
}
finally
{
continue;
}
}
Ini tidak dapat dikompilasi, karena ini menimbulkan kesalahan kompiler CS0157 :
Kontrol tidak dapat meninggalkan isi klausa akhirnya
Mengapa?
continue;
difinally
blok? Bukankah sama dengancontinue;
setelahtry -- catch
blok?finally/continue
ini adalah batasan dari compiler C # :-) Saya juga penasaran dengan alasan dari batasan ini.br
keluarga instruksi cabang tidak dapat melakukannya.Jawaban:
finally
blok berjalan baik pengecualian dilempar atau tidak. Jika pengecualian dilemparkan, apa yang akan terjadicontinue
dilakukan? Anda tidak dapat melanjutkan eksekusi loop, karena pengecualian yang tidak tertangkap akan mentransfer kontrol ke fungsi lain.Bahkan jika tidak ada pengecualian dilempar,
finally
akan berjalan ketika pernyataan transfer kontrol lain di dalam blok coba / tangkap dijalankan, sepertireturn
, misalnya, yang membawa masalah yang sama.Singkatnya, dengan semantik
finally
tidak masuk akal untuk mengizinkan transfer kontrol dari dalam afinally
blok ke luarnya.Mendukung ini dengan beberapa semantik alternatif akan lebih membingungkan daripada membantu, karena ada solusi sederhana yang membuat perilaku yang diinginkan menjadi lebih jelas. Jadi Anda mendapatkan kesalahan, dan dipaksa untuk memikirkan masalah Anda dengan benar. Ini adalah ide umum "melemparkan Anda ke lubang kesuksesan" yang terjadi di C #.
Jika Anda ingin mengabaikan pengecualian (lebih sering daripada bukan bukan ide yang buruk) dan terus menjalankan loop, gunakan blok catch all:
Jika Anda ingin
continue
hanya ketika tidak ada pengecualian yang tidak tertangkap dilemparkan, cukup letakkan dicontinue
luar blok percobaan.sumber
continue
dalamfinally
blok akan baik-baik saja jika hanya melanjutkan loop lokal yang didefinisikan di dalamfinally
blok. Namun, dalam pertanyaan, ia mencoba melanjutkan loop "luar". Pernyataan serupa yang tidak dapat Anda miliki di dalamfinally
blok adalahreturn
,break
(saat keluar dari blok) dangoto
(saat pergi ke label di luarfinally
blok). Untuk diskusi Java terkait, lihat Kembali dari blok akhirnya di Java .Berikut adalah sumber yang dapat dipercaya:
Ini diambil dari MSDN, 8.9.2 Pernyataan lanjutan .
Dokumentasinya mengatakan bahwa:
Dari sini 8.10 Pernyataan percobaan .
sumber
Anda mungkin berpikir itu masuk akal, tetapi sebenarnya tidak masuk akal .
Apa yang ingin Anda lakukan jeda atau lanjutkan saat pengecualian dilemparkan? Tim penyusun C # tidak ingin membuat keputusan sendiri dengan asumsi
break
ataucontinue
. Sebaliknya, mereka memutuskan untuk mengeluh bahwa situasi pengembang akan menjadi ambigu untuk mentransfer kontrolfinally block
.Jadi itu adalah tugas pengembang untuk menyatakan dengan jelas apa yang dia ingin lakukan daripada mengkompilasi asumsi sesuatu yang lain.
Saya harap Anda mengerti mengapa ini tidak bisa dikompilasi!
sumber
finally
pernyataan akan dipengaruhi oleh apakahcatch
telah keluar melalui pengecualian, dan tidak ada mekanisme untuk mengatakan bagaimana seharusnya berinteraksi dengancontinue
. Saya suka teladan Anda, karena ini menunjukkan masalah yang lebih besar.Seperti yang dinyatakan orang lain, tetapi berfokus pada pengecualian, ini benar-benar tentang penanganan pengalihan kendali yang ambigu.
Dalam benak Anda, Anda mungkin memikirkan skenario seperti ini:
Secara teoritis, Anda dapat melacak aliran kontrol dan berkata, ya, ini "ok". Tidak ada pengecualian yang dilemparkan, tidak ada kontrol yang ditransfer. Tapi desainer bahasa C # memiliki masalah lain dalam pikirannya.
Pengecualian yang Dilempar
The Dreaded Goto
Kembalinya
Break up
Jadi kesimpulannya, ya, sementara ada adalah sebuah sedikit kemungkinan menggunakan
continue
dalam situasi di mana kontrol tidak ditransfer, tapi bagus (sebagian?) Dari kasus melibatkan pengecualian ataureturn
blok. Perancang bahasa merasa ini terlalu ambigu dan (kemungkinan) tidak mungkin untuk memastikan pada waktu kompilasi bahwa Anda hanyacontinue
digunakan dalam kasus di mana aliran kontrol tidak ditransfer.sumber
Secara umum
continue
tidak masuk akal bila digunakan difinally
blok. Lihatlah ini:sumber
continue
pernyataan loop luar tidak masuk akal, tetapi itu tidak berarti itu tidak didukung.item
bisa jadi file, gagal baca ->finally
tutup file.continue
mencegah sisa pemrosesan.Yah, menurutku tidak.
Ketika Anda benar-benar memilikinya
catch(Exception)
maka Anda tidak membutuhkan akhirnya (dan mungkin bahkan tidakcontinue
).Ketika Anda memiliki yang lebih realistis
catch(SomeException)
, apa yang harus terjadi jika pengecualian tidak tertangkap? Andacontinue
ingin pergi ke satu arah, kecuali menangani yang lain.sumber
finally
saat Anda memilikinyacatch
. Ini biasanya digunakan untuk menutup sumber daya dengan cara yang andal.return
pernyataan di dalam bloktry
atau Andacatch
. Apa yang kita lakukan sekarang? Kembali atau lanjutkan pengulangan? (dan jika kita melanjutkan, bagaimana jika itu item terakhir untuk mengulang? Kemudian kita hanya melanjutkan di luarforeach
dan kembali tidak pernah terjadi?) EDIT: Atau bahkangoto
ke label di luar loop, hampir semua tindakan yang mentransfer kontrol di luarforeach
loop .return
di dalam blok. Tetapi biasanya eksekusi berlanjut setelah tangkapan semua.Anda tidak dapat meninggalkan tubuh blok terakhir. Ini termasuk istirahat, kembali dan dalam kasus Anda melanjutkan kata kunci.
sumber
The
finally
blok dapat dijalankan dengan pengecualian menunggu untuk rethrown. Tidaklah masuk akal untuk bisa keluar dari blok (dengancontinue
atau apa pun) tanpa memunculkan kembali pengecualian.Jika Anda ingin melanjutkan pengulangan apa pun yang terjadi, Anda tidak memerlukan pernyataan akhirnya: Tangkap saja pengecualiannya dan jangan lempar ulang.
sumber
finally
berjalan baik pengecualian yang tidak tertangkap dilemparkan atau tidak. Orang lain telah menjelaskan mengapa ini membuatcontinue
tidak masuk akal, tetapi berikut adalah alternatif yang mengikuti semangat dari apa yang tampaknya diminta oleh kode ini. Pada dasarnya,finally { continue; }
mengatakan:(1) dapat dipenuhi dengan menempatkan
continue
di akhir masing-masingcatch
, dan (2) dapat dipenuhi dengan menyimpan pengecualian yang tidak tertangkap untuk dibuang nanti. Anda bisa menulisnya seperti ini:Sebenarnya
finally
mau juga dieksekusi dalam kasus ketiga, dimana tidak ada pengecualian dilempar sama sekali, tertangkap atau tidak tertangkap. Jika itu diinginkan, Anda tentu saja dapat menempatkan satucontinue
setelah blok coba-tangkap alih-alih di dalam masing-masingcatch
.sumber
Secara teknis, ini adalah batasan dari CIL yang mendasarinya. Dari spesifikasi bahasa :
dan
Di halaman dokumen untuk
br
instruksi :Terakhir ini berlaku untuk semua instruksi cabang, termasuk
beq
,brfalse
, dllsumber
Para desainer bahasa hanya tidak ingin (atau tidak bisa) bernalar tentang semantik blok akhirnya diakhiri oleh transfer kontrol.
Satu masalah, atau mungkin masalah kuncinya, adalah bahwa file
finally
blok dijalankan sebagai bagian dari beberapa transfer kontrol non-lokal (pemrosesan pengecualian). Target dari transfer kontrol tersebut bukanlah loop yang melingkupi; pemrosesan pengecualian membatalkan perulangan dan melanjutkan penguliran lebih lanjut.Jika kita memiliki transfer kontrol keluar dari
finally
blok pembersihan, maka transfer kontrol asli sedang "dibajak". Itu dibatalkan, dan kontrol pergi ke tempat lain.Semantik bisa dikerjakan. Bahasa lain memilikinya.
Para perancang C # memutuskan untuk melarang statis, transfer kendali "goto-like", dengan demikian menyederhanakan banyak hal.
Namun, bahkan jika Anda melakukan itu, itu tidak menyelesaikan pertanyaan tentang apa yang terjadi jika transfer dinamis dimulai dari
finally
: bagaimana jika blok terakhir memanggil fungsi, dan fungsi itu melempar? Proses pengecualian asli kemudian "dibajak".Jika Anda memahami semantik bentuk pembajakan kedua ini, tidak ada alasan untuk menyingkirkan jenis pertama. Mereka benar-benar adalah hal yang sama: transfer kontrol adalah transfer kontrol, baik dalam lingkup leksikal yang sama atau tidak.
sumber
finally
menurut definisi harus segera diikuti dengan melanjutkan transfer yang memicunya (pengecualian tidak tertangkapreturn
, dll.). Akan mudah untuk mengizinkan transfer "seperti goto" dalamfinally
(bahasa mana yang melakukan ini?), Tetapi melakukannya berarti itutry { return } finally { ... }
tidak akan kembali , yang sama sekali tidak terduga. Jikafinally
memanggil fungsi yang melempar, itu sebenarnya bukan hal yang sama, karena kita sudah mengharapkan pengecualian dapat terjadi kapan saja dan mengganggu aliran normal.finally
adalah hal yang sama, karena dapat mengakibatkan aliran program yang tidak terduga dan tidak logis. Satu-satunya perbedaan adalah bahwa perancang bahasa, karena tidak dapat menolak semua program di mana blok terakhir dapat mengeluarkan pengecualian yang tidak tertangani, sebaliknya mengizinkan program tersebut untuk dikompilasi dan berharap program dapat menerima konsekuensi apa pun yang mungkin mengikuti pengabaian pengecualian-pembatalan sebelumnya urutan.finally
atau tidak. Menurut logika paragraf terakhir Kaz, fungsi juga harus bebas untuk memengaruhi aliran dalam lingkup pemanggilnya dengan caracontinue
, dll., Karena mereka diizinkan melakukannya melalui pengecualian. Tapi ini tentu saja tidak diperbolehkan; pengecualian adalah satu - satunya pengecualian untuk aturan ini, oleh karena itu dinamakan "pengecualian" (atau "interupsi").finally
blok dapat melanggarnya. Benar-benar situasi yang buruk, yang IMHO seharusnya diselesaikan dengan setidaknya membiarkanfinally
blok tahu tentang pengecualian yang tertunda sehingga mereka dapat membangun objek pengecualian komposit.