Kodėl neįmanoma gauti pajamų bandomojo bloko viduje?

Kad:

 try { Console.WriteLine("Before"); yield return 1; Console.WriteLine("After"); } finally { Console.WriteLine("Done"); } 

finally blokas prasideda, kai visa tai baigiama ( IEnumerator<T> palaiko IDisposable kad būtų galima tai padaryti, net jei numeracija paliekama prieš ją baigiant).

Tačiau tai nėra normalu:

 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); } 

Tarkime (dėl argumentų), kad išimtis iškeliama vienu ar kitu skambučiu į WriteLine viduje WriteLine bloko viduje. Kokia yra problema, susijusi su vykdymo vykdymu catch ?

Žinoma, dalis grįžimo grąžos (šiuo metu) negali mesti nieko, bet kodėl tai turėtų užkirsti kelią uždarymo try / catch kad galėtume tvarkyti išimtis, kurios buvo pasirinktos prieš ar po yield return ?

Atnaujinimas: čia yra įdomus Erico Lippertio komentaras - atrodo, kad jie jau turi pakankamai problemų dėl bandymo įgyvendinimo / pagaliau teisingai!

EDIT: šios klaidos MSDN puslapyje: http://msdn.microsoft.com/en-us/library/cs1x15az.aspx . Tačiau jis nepaaiškina, kodėl.

75
06 дек. nustatė Daniel Earwicker 06 d. 2008-12-06 18:14 '08 at 18:14 2008-12-06 18:14
@ 5 atsakymai

Įtariu, kad tai yra praktiškumo, o ne galimybių klausimas. Įtariu, kad yra labai, labai nedaug kartų, kai šis apribojimas iš tikrųjų yra problema, kurios negalima išvengti - bet papildomas kompiliatoriaus sudėtingumas būtų labai svarbus.

Yra keletas tokių dalykų, kuriuos jau sutikau:

  • Atributai negali būti bendrinami
  • „X“ nepavyko gauti iš XY (įdėta klasė X)
  • Iteratorius blokuoja viešųjų laukų naudojimą sukurtose klasėse

Kiekvienu iš šių atvejų būtų šiek tiek daugiau laisvės dėl papildomo kompiliatoriaus sudėtingumo. Komanda pasirinko pragmatišką pasirinkimą, kuriam aš juos sveikinu - norėčiau, kad šiek tiek griežtesnė kalba būtų 99,9% tikslios kompiliatoriaus (taip, yra klaidų, į vieną, SO, tik kitą dieną), nei lankstesnė kalba, kuri nėra galėtų tinkamai sudaryti.

EDIT: Čia yra pseudo įrodymas, kodėl tai įmanoma.

Apsvarstykite, kad:

  • Galite įsitikinti, kad grįžtama grįžimo dalis nesukuria išimties (pirmiausia apskaičiuokite vertę, o tada tiesiog nustatote lauką ir grįžkite „tiesa“)
  • Jums leidžiama pabandyti / sugauti, kuri nenaudoja grįžimo grįžimo į iteratoriaus bloką.
  • Visi vietiniai kintamieji iteratoriaus bloke yra generuojamo tipo egzemplioriniai kintamieji, todėl galite laisvai perkelti kodą į naujus metodus.

Dabar konvertuokite:

 try { Console.WriteLine("a"); yield return 10; Console.WriteLine("b"); } catch (Something e) { Console.WriteLine("Catch block"); } Console.WriteLine("Post"); 

(pseudokodo tipas):

 case just_before_try_state: try { Console.WriteLine("a"); } catch (Something e) { CatchBlock(); goto case post; } __current = 10; return true; case just_after_yield_return: try { Console.WriteLine("b"); } catch (Something e) { CatchBlock(); } goto case post; case post; Console.WriteLine("Post"); void CatchBlock() { Console.WriteLine("Catch block"); } 

Vienintelis dubliavimas yra konfigūruoti bandomuosius / sugavimus blokus - bet tai gali padaryti kompiliatorius.

Galbūt čia kažką praleidau - jei taip, leiskite man žinoti!

41
07 дек. Atsakymą pateikė Jon Skeet, 07 gruodis 2008-12-07 00:07 '08 at 0:07 2008-12-07 00:07

Visos iteratoriaus apibrėžties yield konvertuojamos į būsenos kompiuterio būseną, kuri efektyviai naudoja switch kad išeitų būsenas. Jei jis sugeneravo kodą, skirtą yield bandant / sugauti, jis turėtų pasikartoti viską, kas try bloko dalyje, kiekvienam yield , išskyrus visus kitus šio bloko yield . Tai ne visuomet įmanoma, ypač jei vienas yield priklauso nuo ankstesnio.

5
06 дек. Mark Cidade atsakymas 06 Dec 2008-12-06 19:15 '08 at 7:15 pm 2008-12-06 19:15

Siūlyčiau, kad dėl to, kad skambučių kaminas gauna žaizdą / atsipalaidavimą, kai grįžtate iš skaitiklio, bandymo / sugavimo blokas negali iš tikrųjų „sugauti“ išimtį. (kadangi grįžimo pelningumo blokas nėra kamino, nors jis sukūrė iteracijos bloką)

Norėdami sužinoti apie tai, ką aš kalbu apie iteratoriaus bloko ir supratimo nustatymą naudojant šį iteratorių. Pažiūrėkite, ką skambučių kaminas atrodo foreach bloko viduje, ir tada patikrinkite jį iteratoriaus bandomojo / galutinio bloko viduje.

2
06 дек. atsakymą pateikė Radu094 06 dec. 2008-12-06 18:43 '08 at 18:43 2008-12-06 18:43

Aš sutikau, kad atsakymas į „INVINCIBLE SKEET“ būtų priimtas tol, kol kažkas iš „Microsoft“ atėjo į šią idėją užpilti šaltu vandeniu. Bet aš nesutinku su klausimo dalimi - žinoma, tinkamas kompiliatorius yra svarbesnis už pilną, bet C # kompiliatorius jau yra labai protingas, suskirstydamas šią konversiją mums kiek įmanoma. Šiek tiek daugiau išsamumo šiuo atveju palengvins kalbos vartojimą, mokys, paaiškins, naudos mažiau ribinių atvejų. Todėl manau, kad bus verta papildomų pastangų. Keletas vaikinų Redmondas subraižo galvas dvi savaites, todėl per ateinantį dešimtmetį milijonai koduotojų gali atsipalaiduoti.

(Aš taip pat apgailestauju, kad yra būdas padaryti yield return išimtį, kuri buvo įtraukta į valstybės mašiną "išorėje" su kodu, kuris kontroliuoja iteraciją, tačiau mano priežastys, dėl kurių noriu, yra gana neaiškios.)

Tiesą sakant, vienas klausimas, kurį aš turiu apie Johno atsakymą, yra grąžinimo deklaracijos pateikimas.

Akivaizdu, kad 10 derlius nėra toks blogas. Bet tai būtų blogai:

 yield return File.ReadAllText("c:\\missing.txt").Length; 

Taigi, nėra prasmės tai įvertinti ankstesniame bandymo / sugavimo bloke:

 case just_before_try_state: try { Console.WriteLine("a"); __current = File.ReadAllText("c:\\missing.txt").Length; } catch (Something e) { CatchBlock(); goto case post; } return true; 

Kita problema yra įdėtos bandymo / sugavimo blokai ir pakartotinės išimtys:

 try { Console.WriteLine("x"); try { Console.WriteLine("a"); yield return 10; Console.WriteLine("b"); } catch (Something e) { Console.WriteLine("y"); if ((DateTime.Now.Second % 2) == 0) throw; } } catch (Something e) { Console.WriteLine("Catch block"); } Console.WriteLine("Post"); 

Bet aš tikiu, kad tai įmanoma ...

2
07 дек. Atsakyti Dan Earwicker 07 Dec 2008-12-07 03:38 '08 at 3:38 2008-12-07 03:38

tiems, kurie naudoja vienybę:

 yield return new WaitForSeconds(startWait); while (numWaves < 4  _myPauseState) { for (int i = 0; i < hazardCount;) { //spawn code } yield return new WaitForSeconds(waveWait); numWaves++; } 

tikrai įmanoma viduje ienumerator

-5
16 февр. Atsakymas pateikiamas Zizzy 16 vasario mėn. 2014-02-16 19:50 '14, 19:50, 2014-02-16 19:50