Berikut ini tidak masalah:
try
{
Console.WriteLine("Before");
yield return 1;
Console.WriteLine("After");
}
finally
{
Console.WriteLine("Done");
}
The finally
blok berjalan ketika semuanya telah selesai mengeksekusi ( IEnumerator<T>
mendukung IDisposable
untuk menyediakan cara untuk memastikan ini bahkan ketika pencacahan ditinggalkan sebelum selesai).
Tapi ini tidak baik:
try
{
Console.WriteLine("Before");
yield return 1; // error CS1626: Cannot yield a value in the body of a try block with a catch clause
Console.WriteLine("After");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Misalkan (demi argumen) bahwa pengecualian dilemparkan oleh salah satu WriteLine
panggilan di dalam blok percobaan. Apa masalah melanjutkan eksekusi di catch
blok?
Tentu saja, bagian pengembalian hasil (saat ini) tidak dapat melempar apa pun, tetapi mengapa hal itu menghentikan kami dari memiliki penutup try
/ catch
untuk menangani pengecualian yang dilemparkan sebelum atau sesudah yield return
?
Pembaruan: Ada komentar menarik dari Eric Lippert di sini - tampaknya mereka sudah memiliki cukup masalah dalam menerapkan perilaku coba / akhirnya dengan benar!
EDIT: Halaman MSDN tentang kesalahan ini adalah: http://msdn.microsoft.com/en-us/library/cs1x15az.aspx . Itu tidak menjelaskan mengapa.
sumber
Jawaban:
Saya menduga ini adalah masalah kepraktisan daripada kelayakan. Saya menduga ada sangat, sangat sedikit waktu di mana pembatasan ini sebenarnya merupakan masalah yang tidak dapat diatasi - tetapi kompleksitas tambahan dalam kompiler akan menjadi sangat signifikan.
Ada beberapa hal seperti ini yang pernah saya temui:
Dalam setiap kasus ini, dimungkinkan untuk mendapatkan sedikit lebih banyak kebebasan, dengan biaya kerumitan ekstra dalam kompiler. Tim membuat pilihan pragmatis, yang saya tepuk tangani mereka - saya lebih suka memiliki bahasa yang sedikit lebih ketat dengan kompiler akurat 99,9% (ya, ada bug; saya menemukan satu di SO beberapa hari yang lalu) daripada lebih bahasa fleksibel yang tidak dapat dikompilasi dengan benar.
EDIT: Berikut adalah bukti semu tentang bagaimana mengapa itu layak.
Pertimbangkan bahwa:
Sekarang ubah:
menjadi (semacam pseudo-code):
Satu-satunya duplikasi adalah dalam menyiapkan blok coba / tangkap - tetapi itu adalah sesuatu yang pasti dapat dilakukan oleh kompilator.
Saya mungkin melewatkan sesuatu di sini - jika demikian, beri tahu saya!
sumber
using
danforeach
. Misalnya:try{foreach (string s in c){yield return s;}}catch(Exception){}
yield
, menurut saya, karena kode spageti yang harus Anda tulis untuk mengatasinya.yield
IMO - itu jauh dari sangat .Semua
yield
pernyataan dalam definisi iterator diubah menjadi keadaan dalam mesin keadaan yang secara efektif menggunakanswitch
pernyataan untuk memajukan keadaan. Jika itu benar - benar menghasilkan kode untukyield
pernyataan dalam coba / tangkap itu harus menduplikasi semua yang ada ditry
blok untuk setiapyield
pernyataan sambil mengecualikan setiapyield
pernyataan lain untuk blok itu. Ini tidak selalu memungkinkan, terutama jika satuyield
pernyataan bergantung pada pernyataan sebelumnya.sumber
Saya akan berspekulasi bahwa karena cara tumpukan panggilan mendapat luka / dibatalkan ketika Anda menghasilkan kembali dari enumerator menjadi tidak mungkin untuk mencoba / menangkap blok untuk benar-benar "menangkap" pengecualian. (karena blok pengembalian hasil tidak ada di tumpukan, meskipun ia berasal dari blok iterasi)
Untuk mendapatkan gambaran tentang apa yang saya bicarakan menyiapkan blok iterator dan foreach menggunakan iterator itu. Periksa seperti apa Call Stack di dalam blok foreach dan kemudian periksa di dalam blok percobaan / akhirnya iterator.
sumber
Saya telah menerima jawaban THE INVINCIBLE SKEET sampai seseorang dari Microsoft datang untuk menuangkan ide tersebut. Tetapi saya tidak setuju dengan bagian masalah pendapat - tentu saja kompilator yang benar lebih penting daripada yang lengkap, tetapi kompilator C # sudah sangat pintar dalam memilah transformasi ini untuk kita sejauh yang dilakukannya. Sedikit lebih banyak kelengkapan dalam hal ini akan membuat bahasa lebih mudah digunakan, diajarkan, dijelaskan, dengan kasus tepi atau gotcha yang lebih sedikit. Jadi saya pikir itu akan sepadan dengan usaha ekstra. Beberapa orang di Redmond menggaruk-garuk kepala selama dua minggu, dan sebagai hasilnya jutaan pembuat kode selama dekade berikutnya dapat sedikit lebih santai.
(Saya juga menyimpan keinginan kotor agar ada cara untuk membuat
yield return
pengecualian yang telah dimasukkan ke dalam mesin negara "dari luar", dengan kode yang mendorong pengulangan. Tapi alasan saya menginginkan ini cukup kabur.)Sebenarnya satu pertanyaan yang saya miliki tentang jawaban Jon adalah berkaitan dengan ekspresi pengembalian hasil yang dilempar.
Jelas hasil pengembalian 10 tidak terlalu buruk. Tapi ini buruk:
Jadi, bukankah lebih masuk akal untuk mengevaluasi ini di dalam blok coba / tangkap sebelumnya:
Masalah selanjutnya adalah blok coba / tangkap bersarang dan pengecualian yang muncul kembali:
Tapi saya yakin itu mungkin ...
sumber