2010-05-03 9 views

Odpowiedz

13

Wydajność plonów nie dociera do kodu, dopóki nie będzie potrzebna.

Na przykład, kod:

public IEnumerable<int> GetInts() 
{ 
    yield return 1; 
    yield return 2; 
    yield return 3; 
} 

rzeczywiście kompilacji do zagnieżdżonej klasy realizujący IEnumerable<int> i ciało GetInts() powróci wystąpienia tej klasy.

Korzystanie reflektora widać:

public IEnumerable<int> GetInts() 
{ 
    <GetInts>d__6d d__d = new <GetInts>d__6d(-2); 
    d__d.<>4__this = this; 
    return d__d; 
} 

Edit - dodając więcej informacji o GetInts realizacji:
Droga ta realizacja sprawia, że ​​jest leniwy oparty jest na metodzie EnumeratorMoveNext(). Gdy generowana jest wyliczalna klasa zagnieżdżona (<GetInts>d__6d w przykładzie), ma ona stan i dla każdego stanu jest połączona wartość (jest to prosty przypadek, w bardziej zaawansowanych przypadkach wartość będzie oceniana, gdy kod osiągnie stan). Jeśli spojrzymy w kodzie MoveNext() z <GetInts>d__6d zobaczymy stan:

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

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

     case 2: 
      this.<>1__state = -1; 
      this.<>2__current = 3; 
      this.<>1__state = 3; 
      return true; 

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

Gdy wyliczający jest proszony dla bieżącego obiektu zwraca obiekt, który jest podłączony do obecnego stanu.

W celu wykazania, że ​​kod jest oceniany tylko wtedy, gdy jest to wymagane można spojrzeć na ten przykład:

[TestFixture] 
public class YieldExample 
{ 
    private int flag = 0; 
    public IEnumerable<int> GetInts() 
    { 
     yield return 1; 
     flag = 1; 
     yield return 2; 
     flag = 2; 
     yield return 3; 
     flag = 3; 
    } 

    [Test] 
    public void Test() 
    { 
     int expectedFlag = 0; 
     foreach (var i in GetInts()) 
     { 
      Assert.That(flag, Is.EqualTo(expectedFlag)); 
      expectedFlag++; 
     } 

     Assert.That(flag, Is.EqualTo(expectedFlag)); 
    } 
} 

Mam nadzieję, że nieco bardziej wyraźne. Polecam przyjrzeć się kodowi z Reflectorem i obserwować skompilowany kod podczas zmiany kodu "yield".

+0

@ Eliada: proszę podać więcej szczegółów na temat GetInts(). –

+0

@masoud ramezani, dodano więcej informacji o klasie zagnieżdżonej GetInts. – Elisha

+0

Dziękuję za odpowiedź. –

4

Zasadniczo iteratory implementated za pomocą yield oświadczenia są kompilowane do klasy, która implementuje state machine.

Jeśli nigdy nie będziesz foreach (= powtórz i używaj) zwróconego IEnumerable<T>, kod nigdy nie zostanie faktycznie wykonany. A jeśli to zrobisz, zostanie wykonany tylko minimalny kod potrzebny do określenia następnej wartości do zwrócenia, tylko w celu wznowienia wykonywania, gdy zażąda kolejnej wartości.

Możesz zobaczyć to zachowanie, gdy pojedynczy krok takiego kodu w debugerze. Spróbuj przynajmniej raz: myślę, że to jest pouczające, aby zobaczyć to krok po kroku.

Powiązane problemy