Несколько перечислителей для IEnumerable

У меня есть коллекция, сделанная на заказ, в которой есть много способов генерации объектов.
Он может генерировать все, один объект за раз или N объектов за один раз.
Я хотел бы иметь возможность переключаться между реализациями генерации во время выполнения, и даже, возможно, создавать новые.
Я ищу что-то с таким синтаксисом:

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

Мои проблемы:
Я не знаю что EnumerateAs() вернуть? Я предполагаю, что это IEnumerator, но будет ли он по-прежнему перечислителем моего списка?
LazyEnumerator наследуется от IEnumerator?
Как он узнал о myCollection?

3 ответа

Решение

Возвращаемое значение вашего EnumerateAs() должно быть IEnumerable<T>где T - тип объекта, который содержится в вашей коллекции. Я рекомендую прочитать больше о доходности, поскольку это может помочь вам понять, как работает перечисление. Класса по умолчанию для предоставления перечисления "стратегия" не существует, но вы можете легко реализовать что-то вроде этого, используя возвращение урожая для базовой коллекции различными способами.

Из вашего вопроса неясно, как стратегии перечисления будут взаимодействовать с вашим классом коллекции. Похоже, что вы могли бы после чего-то вроде:

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
    }
}

Обратите внимание, что вы также можете заменить класс стратегии простой стратегией. Func<MyCollection, IEnumerable<T>>, но приведенный выше наиболее точно соответствует желаемому синтаксису.

Я бы посоветовал вам начать с создания функций GetEnumeratorInFirstStyle, GetEnumeratorInSecondStyle и т. Д. (Конечно, используйте имена, подходящие для вашего приложения), а затем создайте новые структуры, например (например, в синтаксисе vb, но они должны быть легко конвертируемыми в C#):

Класс enumTest
    Функция GetEnumeratorInFirstStyle() As IEnumerator(Of Integer)
        Return Enumerable.Empty(Of Integer)() 'Реальный код сделает что-то лучше
    Конечная функция
    Частная структура FirstStyleEnumerable
        Реализует IEnumerable(Of Integer)

        Приватный myEnumTest As enumTest

        Открытая функция GetEnumerator() As System.Collections.Generic.IEnumerator(Of Integer) Реализует System.Collections.Generic.IEnumerable(Of Integer).GetEnumerator
            Вернуть myEnumTest.GetEnumeratorInFirstStyle
        Конечная функция

        Открытая функция GetEnumerator1() As System.Collections.IEnumerator Реализует System.Collections.IEnumerable.GetEnumerator
            Вернуть myEnumTest.GetEnumeratorInFirstStyle
        Конечная функция

        Sub New(ByVal newEnumTest As enumTest)
            myEnumTest = newEnumTest
        End Sub
    Конечная структура
    Открытое свойство ReadOnly AsFirstStyleEnumerable As IEnumerable(Of Integer)
        Получить
            Возврат нового FirstStyleEnumerable(Me)
        Конец получить
    Конечная недвижимость
Конечный класс

Обратите внимание, что используется структура, а не класс, поскольку использование класса потребует создания нового объекта кучи и добавления дополнительного уровня косвенности к его доступу; реальная цель структуры - позволить ей реализовать "отличный" IEnumerable от инкапсулированного объекта. Кстати, можно было бы использовать дженерики с классами маркеров, чтобы избежать необходимости вручную определять новую структуру FirstStyleEnumerator для каждого варианта перечисления. Я не уверен, будет ли это чище или более запутанным, хотя.

Интерфейс IQualifiedEnumerable(Of T, U)
    Функция GetEnumerator () как IEnumerable(Of U)
Конец интерфейса

Структура QualifiedEnumerableWrapper(Of T, U)
    Реализует IEnumerable(Of U)
    Приватный myEnumerable As IQualifiedEnumerable(Of T, U)

    Открытая функция GetEnumerator() As System.Collections.Generic.IEnumerator(Of U) Реализует System.Collections.Generic.IEnumerable(Of U).GetEnumerator
        Вернуть myEnumerable.GetEnumerator
    Конечная функция

    Открытая функция GetEnumerator1() As System.Collections.IEnumerator Реализует System.Collections.IEnumerable.GetEnumerator
        Вернуть myEnumerable.GetEnumerator
    Конечная функция

    Sub New(ByVal newEnumerable As IQualifiedEnumerable(Of T, U))
        myEnumerable = newEnumerable
    End Sub
Конечная структура

Класс EnumTest2
    Реализует IQualifiedEnumerable(Of FirstEnumerationStyle, Integer)
    Реализует IQualifiedEnumerable(Of SecondEnumerationStyle, Integer)

    Частный класс FirstEnumerationStyle     'Маркерные классы для обобщений
    Конечный класс
    Частный класс SecondEnumerationStyle
    Конечный класс

    Закрытая функция GetFirstStyleEnumerator() как System.Collections.Generic.IEnumerable(Of Integer) Реализует IQualifiedEnumerable(Of FirstEnumerationStyle, Integer).GetEnumerator
        Вернуть Enumerable.Empty (Of Integer) ()
    Конечная функция

    Закрытая функция GetSecondStyleEnumerator() как System.Collections.Generic.IEnumerable(Of Integer) Реализует IQualifiedEnumerable(Of SecondEnumerationStyle, Integer).GetEnumerator
        Вернуть Enumerable.Empty (Of Integer) ()
    Конечная функция

    Открытое свойство ReadOnly AsFirstStyleEnumerable As IEnumerable(Of Integer)
        Получить
            Возврат нового QualifiedEnumerableWrapper(Of FirstEnumerationStyle, Integer)
        Конец получить
    Конечная недвижимость

    Открытое свойство ReadOnly AsSecondStyleEnumerable As IEnumerable(Of Integer)
        Получить
            Возврат нового QualifiedEnumerableWrapper(Of SecondEnumerationStyle, Integer)
        Конец получить
    Конечная недвижимость
Конечный класс

Здесь определения интерфейса и структуры полностью универсальны; добавление каждого дополнительного метода перечисления в класс потребовало бы добавления функции для возврата ее перечислителя и свойства для возврата QualifiedEnumerableWrapper соответствующего типа.

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
}

в вашем классе вы можете:

    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

очень гибкий

Другие вопросы по тегам