Как мне предотвратить, чтобы перечислитель агрегировал все элементы? - PullRequest
0 голосов
/ 25 октября 2019

У меня есть простая функция, которую я просто привожу здесь в качестве примера. Это бесконечное перечисление!

public static IEnumerable<int> Roll()
{
    while (true)
    {
        yield return new Random().Next(1, 6);
    }
}

И я хочу, чтобы эта функция продолжала показывать определенное количество тактов, поэтому у меня есть этот метод:

    public static List<T> Ticks<T>(this IEnumerable<T> data, int time)
    {
        var list = new List<T>();
        Stopwatch timer = null;
        foreach (var item in data)
        {
            if (timer == null)
            {
                timer = Stopwatch.StartNew();
            }

            if (timer.ElapsedTicks < time)
            {
                list.Add(item);
            }
            else
            {
                break;
            }
        }
        timer?.Stop();
        return list;
    }

Но я не совсемнапример, как я должен был написать это, поскольку у меня есть что-то более похожее на это:

    public static List<T> Ticks2<T>(this IEnumerable<T> data, int time)
    {
        var list = new List<T>();
        Stopwatch timer = Stopwatch.StartNew();
        using (var en = data.GetEnumerator())
        {
            while (timer.ElapsedTicks < time && en.MoveNext())
            {
                list.Add(en.Current);
            }
        }
        timer.Stop();
        return list;
    }

Но последняя функция завершится сбоем, так как метод GetEnumerator () объединит все данные и в бесконечном перечислении, которое занимает вечность...
Подожди ... Я ошибаюсь ... Оказалось так быстро, что мой список исчерпал память ... К счастью, он не агрегирует ...
Метод Ticks () работает отлично, кстати! Я могу использовать это:

Console.WriteLine( Watch.Roll().Ticks(10000).AllToString().AllJoin(", ") );

И он будет катиться до тех пор, пока не пройдут 10000 тиков, преобразовать все в строки и объединить все, сохраняя цепочку методов. Это важно, кстати. Я не могу принять ни одного ответа, который нарушит цепочку методов. Имейте в виду, что это всего лишь пример.
И нет, я не могу ничего изменить в этом методе Roll (). Этот метод предназначен только для чтения ...

Итак, как мне переписать этот метод Ticks (), более похожий на метод Ticks2 (), не заканчивая агрегированием всех данных?


Для тех, кто интересуется методами AllToString () и AllJoin:

    public static IEnumerable<string> AllToString<T>(this IEnumerable<T> data) => data.Select(item => item.ToString());
    public static string AllJoin<T>(this IEnumerable<T> data, string separator) => string.Join(separator, data.AllToString());

Эти два метода могут быть полезны для других решений, где вы будете использовать перечисления и хотите выводить строки.

1 Ответ

1 голос
/ 25 октября 2019
public static IEnumerable<T> Ticks<T>( this IEnumerable<T> source, Int64 totalTicks )
{
    Stopwatch sw = Stopwatch.StartNew();

    foreach( T item in source )
    {
        if( sw.ElapsedTicks < totalTicks )
        {
            yield return item;
        }
        else
        {
            yield break;
        }
    }
}

На самом деле вам вообще не нужны никакие пользовательские методы расширения: вы также можете просто использовать TakeWhile вместо Ticks, Select вместо AllToString() и Aggregate вместо AllJoin:

Stopwatch sw = Stopwatch.StartNew();

String all = Watch.Roll()
    .TakeWhile( _ => sw.ElapsedTicks < 10000 )
    .Select( i => i.ToString() )
    .Aggregate(
        new StringBuilder(),
        ( s, sb ) => sb.Append( s ).Append(", "),
        sb => sb.ToString()
    );

Console.WriteLine( all ):
...