1. Funcții implementate prin yield
Randament:
Să aruncăm o privire la codul următor, care implementează o funcție similară cu traversarea unui tablou cu foreach prin yield return, indicând că yield return este folosit și pentru a implementa funcția iteratorului.
Scăpare de curgă:
Privind codul de mai jos, doar 1 și 2 sunt generate, dar nu 3, ceea ce indică faptul că iteratorul a fost oprit de yield break, deci yield break este folosit pentru a termina iterația.
2. Poate fi folosit doar <T><T>în metode, operatori, accesori care trebuie să returneze un IEnumerabil, IEnumerabil, IEnumerator sau IEnumerator.
3. Principiul de implementare al cuvântului cheie yield
Am înlocuit bucla foreach cu o buclă while și am constatat că, deși nu am implementat GetEnumerator(), nici proprietățile MoveNext() și Current ale IEnumeratorului corespunzătoare, am lucrat totuși cu aceste funcții.
În ceea ce privește motivul pentru care este așa, putem folosi ILSpy pentru a decompila exe-ul generat și a găsi motivul.
Deoarece decompilarea directă în C# va deveni așa cum este
Prin urmare, am ales să o decompilam în cod IL cu adnotări C#, deși lizibilitatea este slabă, dar putem înțelege principiile în detaliu. Să analizăm mai întâi traducerea programului, o nouă clasă este generată automat în timpul compilației.
Să analizăm mai atent codul, iar EnumerableFuc() returnează această nouă clasă.
Privind implementarea clasei pe care acest cod o generează automat, putem vedea că moștenește IEnumerabil<T>, IEnumerabil, IEnumerator sau IEnumerator, și <T>ar trebui să putem ghici că această nouă clasă este motivul pentru care nu implementăm proprietățile MoveNext() și Current ale IEnumeratorului corespunzătoare, dar putem totuși folosi aceste funcții normal.
Să vedem cum se iterat această clasă, să ne uităm în principal la funcția MoveNext()
Fiecare apel la funcția MoveNext() adaugă 1 la starea și se fac în total 4 iterații, primele trei returnând true și ultimul returnând false, ceea ce înseamnă sfârșitul iterației. Aceste patru iterații corespund instrucțiunilor din enumberableFuc() care sunt împărțite în 4 părți de 3 instrucțiuni return.
Procesul real de iterat cu enumberableFuc() este:
1. Rulați funcția enumberableFuc() pentru a obține o instanță a clasei generate automat de cod. 2. Apoi apelează funcția GetEnumberator() pentru a începe iterarea asupra clasei dobândite ca iterator. 3. De fiecare dată când rulezi MoveNext(), starea crește cu 1, iar instrucțiunea switch îți permite să execuți părți diferite ale codului de fiecare dată când apelezi MoveNext(). 4。 MoveNext() returnează false, terminând. Acest lucru arată, de asemenea, că cuvântul cheie yield este de fapt un zahăr sintactic și, în cele din urmă, este o funcție iterativă implementată prin <T>implementarea interfețelor IEnumberable, IEnumberable, <T>IEnumberator și IEnumberator.
|