2016-05-31 15 views
22

byłem toying wokół z yield i IEnumerable i teraz jestem ciekaw, dlaczego iw jaki sposób następujący fragment działa:Jak wydajność jest przeliczalna?

public class FakeList : IEnumerable<int> 
{ 
    private int one; 
    private int two; 

    public IEnumerator<int> GetEnumerator() 
    { 
     yield return one; 
     yield return two; 
    } 

    IEnumerator IEnumerable.GetEnumerator() 
    { 
     return GetEnumerator(); 
    } 
} 

Teraz w jaki sposób kompilator włączyć to:

public IEnumerator<int> GetEnumerator() 
{ 
    yield return one; 
    yield return two; 
} 

się z IEnumerator<int>?

+3

http://csharpindepth.com/Articles/Chapter6/IteratorBlockImplementation.aspx –

+1

Kompilator magii! – RBarryYoung

Odpowiedz

27

Podczas korzystania z yield return, kompilator generuje dla ciebie klasę modułów wyliczających. Rzeczywisty kod, który jest używany, jest znacznie bardziej złożony niż tylko dwa zwroty. Kompilator dodaje cały niezbędny kod, aby zwrócić moduł wyliczający, który jest iterowany po wynikach z yield return.


ten wygenerowany kod z FakeList.GetEnumerator():

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Diagnostics; 
using System.Runtime.CompilerServices; 

public class FakeList : IEnumerable<int>, IEnumerable 
{ 
    private int one; 
    private int two; 

    [IteratorStateMachine(typeof(<GetEnumerator>d__2))] 
    public IEnumerator<int> GetEnumerator() 
    { 
     yield return this.one; 
     yield return this.two; 
    } 

    IEnumerator IEnumerable.GetEnumerator() => 
     this.GetEnumerator(); 

    [CompilerGenerated] 
    private sealed class <GetEnumerator>d__2 : IEnumerator<int>, IDisposable, IEnumerator 
    { 
     private int <>1__state; 
     private int <>2__current; 
     public FakeList <>4__this; 

     [DebuggerHidden] 
     public <GetEnumerator>d__2(int <>1__state) 
     { 
      this.<>1__state = <>1__state; 
     } 

     private bool MoveNext() 
     { 
      switch (this.<>1__state) 
      { 
       case 0: 
        this.<>1__state = -1; 
        this.<>2__current = this.<>4__this.one; 
        this.<>1__state = 1; 
        return true; 

       case 1: 
        this.<>1__state = -1; 
        this.<>2__current = this.<>4__this.two; 
        this.<>1__state = 2; 
        return true; 

       case 2: 
        this.<>1__state = -1; 
        return false; 
      } 
      return false; 
     } 

     [DebuggerHidden] 
     void IEnumerator.Reset() 
     { 
      throw new NotSupportedException(); 
     } 

     [DebuggerHidden] 
     void IDisposable.Dispose() 
     { 
     } 

     int IEnumerator<int>.Current => 
      this.<>2__current; 

     object IEnumerator.Current => 
      this.<>2__current; 
    } 
} 

Czy widzisz klasę <GetEnumerator>d__2? To jest generowane na podstawie twoich dwóch yield return s.

12

Gdy kompilator widzi yield return lub yield break, przejmuje funkcję i przekazuje jej logikę do klasy, która implementuje automat stanów. Instancja tej klasy jest zwracana po wywołaniu metody.

C# In Depth zawiera sekcję o tym, jak wygląda kod.

5

To generator. Nie mogę powiedzieć, w jaki sposób kompilator działa tyłu, ale kiedy to zrobić:

yield return x; 

Nie pozostawi funkcję jak klasyczna zamian zamiast wrócisz wartość, a następnie kontynuować wykonywanie funkcji.

Jeśli dobrze pamiętam, istnieje niewielka różnica między rzeczywistym przelicznikiem a tym, jeśli nie używasz metody przekształcania generatora w rzeczywistą liczbę przeliczalną (w zależności od tego, co próbujesz osiągnąć), możesz mieć pewne problemy.

+0

Czy możesz rozwinąć różnicę? Ponieważ inni twierdzą, że kompilator generuje przeliczenia – Mafii

+0

, spróbuję go znaleźć. –

+0

znalazłeś coś? – Mafii