2010-05-03 28 views

Respuesta

13

implementación de rendimiento no alcanza el código hasta que sea necesario.

Por ejemplo, este código:

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

realmente compilar en una clase anidada que implementa IEnumerable<int> y el cuerpo de GetInts() devolverá una instancia de esa clase.

El uso del reflector se puede ver:

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

Editar - la adición de más información sobre GetInts aplicación:
La forma en que esta aplicación hace que sea perezosa se basa en el método EnumeratorMoveNext(). Cuando se genera la clase anidada enumerable (<GetInts>d__6d en el ejemplo), tiene un estado y para cada estado se conecta un valor (este es un caso simple, en casos más avanzados, el valor se evaluará cuando el código llegue al estado). Si echamos un vistazo en el código de MoveNext()<GetInts>d__6d veremos el estado:

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

Cuando se le pide al empadronador para el objeto actual que devuelve el objeto que está conectado con el estado actual.

Con el fin de demostrar que el código se evalúa sólo cuando es necesario se puede ver en este ejemplo:

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

espero que sea un poco más clara. Recomiendo echar un vistazo al código con Reflector y observar el código compilado a medida que cambia el código de "rendimiento".

+0

@Elisha: por favor brinde más detalles sobre GetInts(). –

+0

@masoud ramezani, se agregó más información sobre la clase de enumerador anidado GetInts. – Elisha

+0

Gracias por su respuesta completa. –

4

Básicamente iteradores implementated utilizando yield declaraciones se compilan en una clase que implementa un state machine.

Si nunca foreach (= itera y usa) el IEnumerable<T> devuelto, el código nunca se ejecuta. Y si lo hace, solo se ejecuta el código mínimo necesario para determinar el siguiente valor a devolver, solo para reanudar la ejecución cuando se solicita un próximo valor.

En realidad, puede ver este comportamiento que ocurre cuando usted solo paso tal código en el depurador. Pruébalo al menos una vez: creo que es alentador ver que esto suceda paso a paso.

Cuestiones relacionadas