Múltiples enumeradores para un IEnumerable
-
01-10-2019 - |
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?
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
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.