Pregunta

Tengo una colección hecha a medida de que tiene muchos modos de objetos generaciones dentro de ella.
Puede generar todo lo anterior, un objetivo a la vez o n objetos a la vez.
Me gustaría tener la opción de cambiar entre implementaciones de generación en tiempo de ejecución, e incluso tal vez crear otros nuevos.
Busco algo con este tipo de sintaxis:

foreach(var obj in myCollection.EnumerateAs(new LazyEnumerator())
{
   // ...
}

Mis problemas son:
No sé lo que hace el retorno EnumerateAs()? Estoy asumiendo que es IEnumerator pero ¿seguirá siendo el empadronador de mi lista?
¿El LazyEnumerator hereda de IEnumerator?
¿Cómo es consciente de myCollection?

¿Fue útil?

Solución

El valor de retorno de su EnumerateAs() debe ser IEnumerable<T>, donde T es el tipo de objeto que contenía en su colección. Recomiendo leer más sobre yield return , ya que esto le puede ayudar a entender cómo funciona la enumeración. No hay ninguna clase predeterminada para proporcionar una 'estrategia' enumeración, pero se puede implementar fácilmente algo como esto mediante el uso de retorno de rendimiento de la colección subyacente de diversas maneras.

No está claro por su pregunta exactamente cómo las estrategias de enumeración interactuarían con su clase de colección. Parece que es posible que después de algo como:

public interface IEnumerationStrategy<TCollection, T>
{
    IEnumerable<T> Enumerate(TCollection source);
}

public class Quark {}

public class MyCollection
{
    public IEnumerable<Quark> EnumerateAs(IEnumerationStrategy<MyCollection, Quark> strategy)
    {
        return strategy.Enumerate(this);
    }

    //Various special methods needed to implement stategies go here
}

public class SpecialStrategy : IEnumerationStrategy<MyCollection, Quark>
{
    public IEnumerable<Quark> Enumerate(MyCollection source)
    {
        //Use special methods to do custom enumeration via yield return that depends on specifics of MyCollection
    }
}

Tenga en cuenta que también puede sustituir a la clase de estrategia con un simple Func<MyCollection, IEnumerable<T>> estrategia, pero lo anterior coincide con la sintaxis más estrechamente deseada.

Otros consejos

Yo sugeriría que se inicia mediante la creación de funciones GetEnumeratorInFirstStyle, GetEnumeratorInSecondStyle, etc (los nombres de uso adecuado para su aplicación, por supuesto), y luego crear nuevas estructuras algo así como (ejemplo en la sintaxis de VB, pero debe ser fácilmente convertible en C # ):

Class enumTest
    Function GetEnumeratorInFirstStyle() As IEnumerator(Of Integer)
        Return Enumerable.Empty(Of Integer)() ' Real code would do something better
    End Function
    Private Structure FirstStyleEnumerable
        Implements IEnumerable(Of Integer)

        Private myEnumTest As enumTest

        Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of Integer) Implements System.Collections.Generic.IEnumerable(Of Integer).GetEnumerator
            Return myEnumTest.GetEnumeratorInFirstStyle
        End Function

        Public Function GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
            Return myEnumTest.GetEnumeratorInFirstStyle
        End Function

        Sub New(ByVal newEnumTest As enumTest)
            myEnumTest = newEnumTest
        End Sub
    End Structure
    Public ReadOnly Property AsFirstStyleEnumerable As IEnumerable(Of Integer)
        Get
            Return New FirstStyleEnumerable(Me)
        End Get
    End Property
End Class

Tenga en cuenta que una estructura se utiliza en lugar de una clase, ya que el uso de una clase requeriría la creación de un nuevo objeto pila y añadir un nivel adicional de indirección para su acceso; el propósito real de la estructura es para permitir que se implemente un "diferente" IEnumerable en el objeto encapsulado. BTW, sería posible utilizar los genéricos con clases de marcadores para evitar tener que definir manualmente una nueva estructura FirstStyleEnumerator para cada variación en la enumeración. No estoy seguro de si eso sería más limpio o más confuso, sin embargo.

Interface IQualifiedEnumerable(Of T, U)
    Function GetEnumerator() As IEnumerable(Of U)
End Interface

Structure QualifiedEnumerableWrapper(Of T, U)
    Implements IEnumerable(Of U)
    Private myEnumerable As IQualifiedEnumerable(Of T, U)

    Public Function GetEnumerator() As System.Collections.Generic.IEnumerator(Of U) Implements System.Collections.Generic.IEnumerable(Of U).GetEnumerator
        Return myEnumerable.GetEnumerator
    End Function

    Public Function GetEnumerator1() As System.Collections.IEnumerator Implements System.Collections.IEnumerable.GetEnumerator
        Return myEnumerable.GetEnumerator
    End Function

    Sub New(ByVal newEnumerable As IQualifiedEnumerable(Of T, U))
        myEnumerable = newEnumerable
    End Sub
End Structure

Class EnumTest2
    Implements IQualifiedEnumerable(Of FirstEnumerationStyle, Integer)
    Implements IQualifiedEnumerable(Of SecondEnumerationStyle, Integer)

    Private Class FirstEnumerationStyle     ' Marker classes for generics
    End Class
    Private Class SecondEnumerationStyle
    End Class

    Private Function GetFirstStyleEnumerator() As System.Collections.Generic.IEnumerable(Of Integer) Implements IQualifiedEnumerable(Of FirstEnumerationStyle, Integer).GetEnumerator
        Return Enumerable.Empty(Of Integer)()
    End Function

    Private Function GetSecondStyleEnumerator() As System.Collections.Generic.IEnumerable(Of Integer) Implements IQualifiedEnumerable(Of SecondEnumerationStyle, Integer).GetEnumerator
        Return Enumerable.Empty(Of Integer)()
    End Function

    Public ReadOnly Property AsFirstStyleEnumerable As IEnumerable(Of Integer)
        Get
            Return New QualifiedEnumerableWrapper(Of FirstEnumerationStyle, Integer)
        End Get
    End Property

    Public ReadOnly Property AsSecondStyleEnumerable As IEnumerable(Of Integer)
        Get
            Return New QualifiedEnumerableWrapper(Of SecondEnumerationStyle, Integer)
        End Get
    End Property
End Class

Aquí, las definiciones de la interfaz y la estructura son completamente de propósito general; la adición de cada método adicional de enumeración a una clase requeriría la adición de una función para devolver su empadronador, y una propiedad para devolver un QualifiedEnumerableWrapper del tipo apropiado.

public class AltEnumerator : System.Collections.IEnumerable
{

    private System.Collections.IEnumerator _base;

    public AltEnumerator(System.Collections.IEnumerator _pbase)
    {
        _base = _pbase;
    }


    #region IEnumerable Members

    public System.Collections.IEnumerator GetEnumerator()
    {
        return _base ;
    }

    #endregion
}

en su clase puede:

    public AltEnumerator Iterate(IterDIrection How )
    {
        switch (How)
        {
            case TwoDimArray<T>.IterDIrection.RghtLeftTopBottom:
                return new AltEnumerator(GetRightLeft());
        }
        return new AltEnumerator(GetEnumerator());
    }

    private System.Collections.IEnumerator GetRightLeft()
    {
        for (int cndx = PutSlotArray.GetLength(1) - 1; cndx >= 0; cndx--)
            for (int rndx = 0; rndx < PutSlotArray.GetLength(0); rndx++)
                if (PutSlotArray[rndx, cndx] != null)
                    yield return PutSlotArray[rndx, cndx];
    }

    #region IEnumerable Members

    public System.Collections.IEnumerator GetEnumerator()
    {
        foreach (T ps in PutSlotArray)
            if (ps != null)
                yield return ps;
    }

    #endregion

muy Flexable.

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