2009-10-20 28 views
10

Cuando tengo un bloque de códigopatrón de trabajo de rendimiento rendimiento

static void Main() 
{ 

    foreach (int i in YieldDemo.SupplyIntegers()) 
    { 
    Console.WriteLine("{0} is consumed by foreach iteration", i); 
    } 
} 


class YieldDemo 
    { 
    public static IEnumerable<int> SupplyIntegers() 
    { 
     yield return 1; 
     yield return 2; 
      yield return 3; 
     } 
    } 

¿Puedo interpretar el principio detrás de retorno rendimiento como

  1. Principal() llama a los SupplyIntegers()
  2. |1| |2| |3| are stored in contiguous memory block.Pointer of "IEnumerator" Moves to |1|
  3. El control vuelve de SupplyInteger() a Main().
  4. Principal() imprime el valor
  5. Puntero Se mueve a | 2 |, y así sucesivamente.

Aclaraciones:

(1) Normalmente tendremos se permite una sentencia de retorno válida dentro de un function.How hace C# trata cuando devuelven varios rendimiento, el rendimiento de retorno, ... declaraciones están presentes ?

(2) Una vez que se encuentra el retorno, no hay forma de que el control regrese de nuevo a SupplyIntegers(), en caso de que esté permitido, ¿no volverá el Rendimiento desde 1? Quiero decir rendimiento rendimiento 1?

+0

Re preguntar sobre un libro: C# en profundidad (Manning, Skeet), capítulo 6. Es el capítulo de muestra libre, y cubre bloques de iteradores. No es exactamente un libro para principiantes de C# (está lejos de serlo), pero tendrá dificultades para encontrar una mejor referencia sobre este tema. –

+0

Si los privilegios permiten preguntar "¿No has escrito ningún libro?" – user193276

+0

No, no lo he hecho. Hago algunas correcciones para un editor, y de vez en cuando escribo un artículo extraño, etc. Pero no hay ningún libro de mi parte. –

Respuesta

30

No, lejos de eso; Voy a escribir una versión de larga duración para ti ... ¡es demasiado sucio!


Nota que también ayuda si usted entiende que el foreach es en realidad:

using(var iterator = YieldDemo.SupplyIntegers().GetEnumerator()) { 
    int i; 
    while(iterator.MoveNext()) { 
     i = iterator.Current; 
     Console.WriteLine("{0} is consumed by foreach iteration", i); 
    } 
} 

using System; 
using System.Collections; 
using System.Collections.Generic; 
static class Program 
{ 
    static void Main() 
    { 

     foreach (int i in YieldDemo.SupplyIntegers()) 
     { 
      Console.WriteLine("{0} is consumed by foreach iteration", i); 
     } 
    } 
} 

class YieldDemo 
    { 

    public static IEnumerable<int> SupplyIntegers() 
    { 
     return new YieldEnumerable(); 
     } 
    class YieldEnumerable : IEnumerable<int> 
    { 
     public IEnumerator<int> GetEnumerator() 
     { 
      return new YieldIterator(); 
     } 
     IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } 
    } 
    class YieldIterator : IEnumerator<int> 
    { 
     private int state = 0; 
     private int value; 
     public int Current { get { return value; } } 
     object IEnumerator.Current { get { return Current; } } 
     void IEnumerator.Reset() { throw new NotSupportedException(); } 
     void IDisposable.Dispose() { } 
     public bool MoveNext() 
     { 
      switch (state) 
      { 
       case 0: value = 1; state = 1; return true; 
       case 1: value = 2; state = 2; return true; 
       case 2: value = 3; state = 3; return true; 
       default: return false; 
      } 
     } 
    } 
} 

Como se puede ver, se construye una máquina de estados del iterador, con el Estado máquina progresada por MoveNext. He utilizado el patrón con un campo state, como se puede ver cómo esto funcionaría para iteradores más complejos.

Es importante destacar que:

  • cualquier variable en su bloque iterador convertido campos en la máquina de estados
  • si tiene un bloque finally (incluyendo using), que va en los Dispose()
  • porciones de código que lleva a yield return convertido en case (aproximadamente)
  • yield break se convierte en state = -1; return false; (o similar)

La manera en que el compilador C# hace esto es muy complicado, pero hace que escribir iteradores sea muy fácil.

+2

. Es increíble que las personas lo voten antes que en realidad sea una respuesta útil. – Joren

+1

@Joren: Eso es lo bueno que es Marc. ;) Excelente respuesta Marc! – jrista

+1

Es muy simple, cuando Marc dice que va a publicar algo, puedes apostar que va a estar bien. – kemiller2002

3

Es solo una sintaxis sugar, .net genera la clase IEnumerator para usted e implementa los métodos MoveNext, Current y Reset, que genera la clase IEnumarable GetEnumerator de la cual devuelve ese IEnumerator, puede ver las clases de magia por .net reflector o ildasm.

Véase también here

1

en definitiva, (mientras año a la espera de la versión larga mano de orujo) cuando el compilador ve declaraciones de rendimiento, detrás de las escenas que construye una nueva instancia de una clase personalizada para usted que implementa una interfaz llamado IEnumerator, que tiene los métodos Current() y MoveNext(), y realiza un seguimiento de dónde se encuentra actualmente en el proceso de iteración ... En el caso anterior como su ejemplo, también mantendría un registro de los valores en la lista que se enumerará.

2

En pocas palabras, los bloques de iteradores (o los métodos con yield declaraciones, si se puede) se transforman por el compilador en una clase generada por el compilador. Esta clase implementa IEnumerator y la instrucción yield se transforma en un 'estado' para esa clase.

Por ejemplo, esto:

yield return 1; 
yield return 2; 
yield return 3; 

puedes ser transformado en algo similar a:

switch (state) 
{ 
    case 0: goto LABEL_A; 
    case 1: goto LABEL_B; 
    case 2: goto LABEL_C; 
} 
LABEL_A: 
    return 1; 
LABEL_B: 
    return 2; 
LABEL_C: 
    return 3; 

bloques Iterator son pueden ser vistos como máquinas de estado abstraídos. Este código será invocado por los métodos IEnumerator.

-2

¿Recuerdas el dictado en la escuela? Supongamos que tenemos cinco hijos, que vienen para el dictado de palabras en inglés. Tenemos una lista de palabras escritas en una pizarra. Los niños no pueden ver la pizarra, pero los maestros pueden verla. Tenga en cuenta que las palabras se deben dar en el mismo orden para todos los niños. Cada niño es atendido por un maestro diferente.

Pizarra:
      Estados Unidos de América
      Reino Unido
      la India
Preguntas
a. ¿Quiénes son los clientes?
b. ¿Quién es el proveedor de datos?
c. ¿Quién está dictando (iterando o enumerando) la colección?
Respuestas:
a. Niños
b. Pizarra
c. Maestro

en C# términos:

Pizarra - Proveedor de datos - IEnumerable
maestro - iterador - estado de la máquina
Niños - Cliente - ParaCada