2010-04-22 29 views
11

Estoy leyendo este blog: Pipes and filters patternAyúdame a entender el fragmento de código en C#

Estoy confundido por este fragmento de código:

public class Pipeline<T> 
{ 
    private readonly List<IOperation<T>> operations = new List<IOperation<T>>(); 

    public Pipeline<T> Register(IOperation<T> operation) 
    { 
     operations.Add(operation); 
     return this; 
    } 

    public void Execute() 
    { 
     IEnumerable<T> current = new List<T>(); 
     foreach (IOperation<T> operation in operations) 
     { 
      current = operation.Execute(current); 
     } 
     IEnumerator<T> enumerator = current.GetEnumerator(); 
     while (enumerator.MoveNext()); 
    } 
} 

¿cuál es el propósito de esta declaración: tiempo (empadronador. MoveNext());? parece que este código es un noop.

+0

No se puede ver ningún propósito. Si hay algún efecto secundario oculto para llamar a MoveNext en este contexto, esa es una pieza desagradable de código. –

Respuesta

7

Primero considere esto:

IEnumerable<T> current = new List<T>(); 
foreach (IOperation<T> operation in operations) 
{ 
    current = operation.Execute(current); 
} 

Este código parece estar creando enumerables anidados, cada uno de los cuales toma elementos de la anterior, se aplica alguna operación a ellos, y pasa el resultado a la siguiente. Pero solo construye los enumerables. Nada realmente sucede todavía. Está listo para funcionar, almacenado en la variable current. Hay muchas formas de implementar IOperation.Execute, pero podría ser algo como esto.

IEnumerable<T> Execute(IEnumerable<T> ts) 
{ 
    foreach (T t in ts) 
     yield return this.operation(t); // Perform some operation on t. 
} 

Otra opción que se sugiere en el artículo es una especie:

IEnumerable<T> Execute(IEnumerable<T> ts) 
{ 
    // Thank-you LINQ! 
    // This was 10 lines of non-LINQ code in the original article. 
    return ts.OrderBy(t => t.Foo); 
} 

Ahora mira esto:

IEnumerator<T> enumerator = current.GetEnumerator(); 
while (enumerator.MoveNext()); 

En realidad, esto hace que la cadena de operaciones a realizar. Cuando los elementos se solicitan de la enumeración, hace que los elementos del enumerable original pasen a través de la cadena IOperations, cada uno de los cuales realiza alguna operación en ellos. El resultado final se descarta, por lo que solo el efecto secundario de la operación es interesante, como escribir en la consola o iniciar sesión en un archivo. Esto habría sido una manera más sencilla de escribir las dos últimas líneas:

foreach (T t in current) {} 

Otra cosa a observar es que la lista inicial que comienza el proceso es una lista vacía por lo que para que esto tenga sentido algunos casos de T tienen que ser creado dentro de la primera operación. En el artículo, esto se hace pidiendo al usuario que ingrese desde la consola.

+0

¿qué hay de usar .ToList() para realizar las operaciones – Benny

+0

@Benny: Sí, también puede escribir 'current.ToList();' en lugar de las dos últimas líneas. Esto también causaría que la enumeración ocurra. –

+0

Sospecho que todo esto solo puede funcionar porque hay un lugar en el 'Ejecución' incluido en el 'Ejecutar' de la lista' operaciones '. Entonces, lo que estoy diciendo es que esto no se puede hacer sin el uso de 'yield return', porque solo eso causa una evaluación perezosa. ¿Estoy en lo cierto? – kahoon

1
while (enumerator.MoveNext()); 

Dentro del bloque actual de código, no hay efecto (se mueve a través de todos los elementos en la enumeración). El código mostrado no actúa sobre el elemento actual en la enumeración. Lo que podría estar sucediendo es que el método MoveNext() se está moviendo al siguiente elemento y está haciendo algo con los objetos de la colección (actualizando un valor interno, extrayendo el siguiente de la base de datos, etc.). Como el tipo es List<T>, probablemente este no sea el caso, pero en otros casos podría serlo.

+0

entonces, ¿el código * while * adjunto no hace la diferencia? –

+1

'while (enumerator.MoveNext());' es un ciclo completo. Esto atraviesa efectivamente todo el enumerable. –

3

En este caso, el while (enumerator.MoveNext()); simplemente está evaluando todos los artículos devueltos por el IOperation<T> final. Parece un poco confuso, pero el List<T> vacío solo se crea para proporcionar un valor al primer IOperation<T>.

En muchas colecciones esto no causaría nada como usted sugiere, pero dado que estamos hablando del patrón de tuberías y filtros, es probable que el valor final sea algún tipo de iterador que haga que se ejecute el código.Podría ser algo como esto, por ejemplo (suponiendo que es un número entero):

public class WriteToConsoleOperation : IOperation<int> 
{ 
    public IEnumerable<int> Execute(IEnumerable<int> ints) 
    { 
     foreach (var i in ints) 
     { 
      Console.WriteLine(i); 
      yield return i; 
     } 
    } 
} 

Así llamando MoveNext() para cada elemento de la IEnumerator<int> devuelto por este iterador volverá cada uno de los valores (que son ignorados en el while loop) pero también muestra cada uno de los valores en la consola.

¿Tiene sentido?

+0

seguro que tiene sentido. – Benny

Cuestiones relacionadas