Pregunta

I tiene un # clase C que necesita para procesar una secuencia de elementos (IEnumerable<T>) a través de un montón de métodos, así que no puedo simplemente foreach dentro de un método. Me llamo .GetEnumerator() y pasar esta IEnumerator<T> alrededor y funciona muy bien me da la flexibilidad que necesito mientras bucle a través de una única secuencia.

Ahora quiere permitir que otros puedan añadir lógica en este proceso. La forma más natural de hacerlo es darles una interfaz con un método que acepta la IEnumerator<T>. Fácil, hecho esto, y funciona.

Pero me preocupa que esto es un anti-patrón. Ellos tienen que saber que el IEnumerator<T> ya ha tenido .MoveNext() llamada, por lo que pueden .Current simplemente el acceso. Además, no veo ningún precedente para el uso de IEnumerator<T> en las interfaces a implementar.

  1. ¿Qué trampas ¿No soy yo considerando?
  2. ¿Hay otro patrón que me permitirá este mismo eficiente mecanismo (es decir, no quiero múltiples copias que se crean / destruido) sin exponer el IEnumerator<T> sí?

Actualización: Como ya he mencionado en un comentario a continuación: Lo que quiero es una especie de Stream<T> genérico. Tengo que ser capaz de ver efectivamente el artículo siguiente (IEnumerator.Current -> .Peek()) y lo consumen (IEnumerator<T>.MoveNext() -> .Pop()).

utiliza IEnumerator<T> porque se ajustaba a la interfaz factura sabia. Yo prefiero usar tipos BCL común cuando se encajan, pero parecía como si estuviera abusando de éste.

Así que la pregunta 3) ¿Hay una clase que se ajusta a esta necesidad? ¿O debo crear mi propia corriente que ejecuta la pereza IEnumerator<T> internamente? Entonces sería totalmente encapsulado. Me gustaría no utilizar muchas de las colecciones existentes, ya que tienen almacenamiento interno, mientras que me gustaría que el almacenamiento para ser el IEnumerable<T> iteslf.


OK suena como el consenso es que hacer para IEnumerator<T> siendo a menudo un ValueType, así como no saber a priori el estado de la IEnumerator<T>, que es generalmente una mala idea para pasarlo alrededor.

La mejor sugerencia que he oído es crear mi propia clase que se pasa alrededor. ¿Alguna otra sugerencia?

¿Fue útil?

Solución

Si entiendo esto correctamente, tienen una serie de métodos que pueden todos MoveNext llamada en la secuencia y desea que estos métodos para cooperar con cada-otra, por lo que se pasa alrededor de un IEnumerator<T>. Definitivamente hay algo de acoplamiento ajustado aquí, como usted ha mencionado, ya que se espera que el empadronador a estar en un estado particular, a la entrada de cada método. Suena como lo que realmente después aquí es algo así como la clase Stream, que es a la vez una colección (más o menos) y un iterador (tiene una noción de la ubicación actual). Me gustaría envolver su iteración y cualquier otro estado que necesita en su propia clase y tienen los diversos métodos como miembros de esa clase

Otros consejos

debe definitivamente no pase la IEnumerator<T> alrededor. Aparte de todo lo demás, podría tener algunos muy efectos extraños en algunos casos. Lo que se puede esperar que ocurra aquí, por ejemplo?

using System;
using System.Collections.Generic;

class Test
{
    static void ShowCurrentAndNext(IEnumerator<int> iterator)        
    {
        Console.WriteLine("ShowCurrentAndNext");
        Console.WriteLine(iterator.Current);
        iterator.MoveNext(); // Let's assume it returns true
        Console.WriteLine(iterator.Current);
    }

    static void Main()
    {
        List<int> list = new List<int> { 1, 2, 3, 4, 5 };
        using (var iterator = list.GetEnumerator())
        {
            iterator.MoveNext(); // Get things going
            ShowCurrentAndNext(iterator);
            ShowCurrentAndNext(iterator);
            ShowCurrentAndNext(iterator);
        }
    }
}

Un par de cambios para tratar:

using (List<int>.Enumerator iterator = list.GetEnumerator())

y

using (IEnumerator<int> iterator = list.GetEnumerator())

Trate de predecir los resultados en cada caso:)

Ahora es cierto que es un ejemplo particularmente mal, pero demuestra algunos casos de esquina asociados con el paso de estado en torno mutable. Me animo fuertemente para llevar a cabo todas sus iteración en un método "central" que pone en otros métodos apropiados acaba con el valor actual.

Yo aconsejaría fuertemente contra el que pasa el empadronador en torno; ¿qué razón tiene usted para esto, aparte de necesitar el valor actual?

A menos que me falta algo obvio, recomiendo tener sus funciones de utilidad simplemente toman el tipo que está enumerando como un parámetro, y luego tener un solo bucle foreach externa que se encarga de la enumeración real.

Tal vez usted puede proporcionar alguna información adicional en cuanto a por qué usted ha hecho esta decisión de diseño hasta ahora.

Me suena como que podría beneficiarse del uso de un evento para que pueda empujar una notificación de artículos a procesar a los oyentes. Los eventos regulares de .NET se manejan en el orden en que está suscrito, por lo que podría ir a dar un enfoque más explícita si se requiere el pedido.

También les gusta mirar a Marco reactiva.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top