IEnumerable & л; Т & GT; к IEnumerable & lt; IEnumerable & lt; T & gt; & gt; [Дубликат]

Сервер (база данных / сеть) не может инициировать соединение - только клиент может. Поэтому вам придется опросить базу данных до тех пор, пока не появится обновление. Вы можете создать веб-сервис, который проверяет базу данных и какой jQuery использует.

Изменить: Я стою исправлено. Можно поддерживать соединение AJAX открытым до тех пор, пока сервер не «подталкивает» данные к нему. См .: http://en.wikipedia.org/wiki/Reverse_Ajax

И, по-видимому, это действительно опрос: http://en.wikipedia.org/wiki / Push_technology # Long_polling . Если сервер еще не имеет данных для отправки, он держит соединение открытым до тех пор, пока оно не произойдет. Это не «чистая» технология push, потому что клиент не имеет порта прослушивания, к которому подключается сервер. Эффект схож, однако.

Редактировать 2: Вернемся к ответу на ваш вопрос ... Вам нужно будет выбрать, как «опросить» веб-службу. Затем веб-службе необходимо проверить базу данных, чтобы узнать, есть ли обновления. Проверка базы данных на обновления может оказаться сложной и действительно зависит от ваших требований. Вы можете запустить SQL-запрос, чтобы узнать, что-то изменилось, но как вы узнали? Для сравнения вам понадобится какой-то параметр (обычно дата). Если все сделано неправильно, вы можете пропустить некоторые обновления или иметь несколько обращений за одним обновлением. То, что сказал автократия, было бы хорошим способом получить уведомление об обновлениях. Вы можете сохранить этот список в базе данных, в памяти и т. Д. И очистить его, когда клиент получит обновления.

63
задан BlakeH 5 December 2012 в 22:27
поделиться

11 ответов

Вам не нужно писать код. Используйте MoreLINQ Пакетный метод, который поставляет исходную последовательность в размерные ковши (MoreLINQ доступен в виде пакета NuGet, который вы можете установить):

int size = 10;
var batches = sequence.Batch(size);

Что реализовано как:

public static IEnumerable<IEnumerable<TSource>> Batch<TSource>(
                  this IEnumerable<TSource> source, int size)
{
    TSource[] bucket = null;
    var count = 0;

    foreach (var item in source)
    {
        if (bucket == null)
            bucket = new TSource[size];

        bucket[count++] = item;
        if (count != size)
            continue;

        yield return bucket;

        bucket = null;
        count = 0;
    }

    if (bucket != null && count > 0)
        yield return bucket.Take(count);
}
66
ответ дан Chris Marisic 16 August 2018 в 14:13
поделиться
  • 1
    4 байта на элемент выполняет ужасно ? Есть ли у вас несколько тестов, которые показывают, что означает ужасно ? Если вы загружаете миллионы предметов в память, я бы этого не сделал. Использовать оповещение на стороне сервера – Sergey Berezovskiy 11 July 2013 в 20:26
  • 2
    Я не хочу вас обидеть, но есть более простые решения, которые не накапливаются вообще. Кроме того, это будет выделять пространство даже для несуществующих элементов: Batch(new int[] { 1, 2 }, 1000000) – Nick Whaley 12 July 2013 в 19:56
  • 3
    @NickWhaley хорошо, согласитесь с вами, что будет выделено дополнительное пространство, но в реальной жизни у вас обычно есть только противоположная ситуация - список из 1000 предметов, которые должны идти в партиях по 50 :) – Sergey Berezovskiy 12 July 2013 в 20:02
  • 4
    Да, ситуация обычно должна быть другой, но в реальной жизни это могут быть пользовательские входы. – Nick Whaley 15 July 2013 в 14:29
  • 5
    Это идеальное решение. В реальной жизни вы: проверяете ввод пользователя, обрабатываете партии как целые коллекции элементов (которые накапливают элементы в любом случае), и часто обрабатывают партии параллельно (что не поддерживается подходом итератора и будет неприятным сюрпризом, если вы не знаете подробности реализации). – Michael Petito 19 May 2014 в 22:07

Это полностью ленивая, низкая накладная, однофункциональная реализация пакета, которая не делает никакого накопления. Основываясь на (и исправляет проблемы) в решении Ника Уэйли с помощью EricRoller.

Итерация происходит непосредственно из основного IEnumerable, поэтому элементы должны быть перечислены в строгом порядке и не доступны больше чем единожды. Если некоторые элементы не потребляются во внутреннем цикле, они отбрасываются (и попытка получить к ним доступ через сохраненный итератор будет бросать InvalidOperationException: Enumeration already finished.).

Вы можете протестировать полный образец в .NET Fiddle .

public static class BatchLinq
{
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
    {
        if (size <= 0)
            throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");
        using (var enumerator = source.GetEnumerator())
            while (enumerator.MoveNext())
            {
                int i = 0;
                // Batch is a local function closing over `i` and `enumerator` that
                // executes the inner batch enumeration
                IEnumerable<T> Batch()
                {
                    do yield return enumerator.Current;
                    while (++i < size && enumerator.MoveNext());
                }

                yield return Batch();
                while (++i < size && enumerator.MoveNext()); // discard skipped items
            }
    }
}
3
ответ дан infogulch 16 August 2018 в 14:13
поделиться
  • 1
    Это единственная полностью ленивая реализация. В соответствии с реализацией python itertools.GroupBy. – Eric Roller 3 January 2018 в 19:25
  • 2
    Вы можете исключить проверку для done, просто позвонив e.Count() после yield return e. Вам нужно будет перестроить цикл в BatchInner, чтобы не вызывать неопределенное поведение source.Current, если i >= size. Это устранит необходимость выделения нового BatchInner для каждой партии. – Eric Roller 3 January 2018 в 20:02
  • 3
    @EricRoller какое неопределенное поведение? Цикл проверяет на "i & lt; Размер & Quot; уже. Ухаживать за своими идеями в другой скрипке? – infogulch 5 April 2018 в 18:37
  • 4
    Вы правы, вам все равно нужно собирать информацию о ходе каждой партии. Я нашел ошибку в вашем коде, если вы попытаетесь получить второй элемент из каждой партии: скрипт ошибки . Исправлена ​​реализация без отдельного класса (с использованием C # 7): fixed fiddle . Обратите внимание, что я ожидаю, что CLR все равно будет создавать локальную функцию один раз за цикл, чтобы захватить переменную i, поэтому это не обязательно более эффективно, чем определение отдельного класса, но я думаю немного чище. – Eric Roller 7 April 2018 в 01:56
  • 5
    Очень хорошо! Я попытался какое-то время, но сдался, я забыл о местных функциях, это отличная функция. Гораздо чище, чем отдельный класс imo. Поскольку i на самом деле не привязана к телу цикла (до тех пор, пока вы установите i=0 на каждую итерацию), вы можете вытащить его с помощью определения Batch (), чтобы избежать дополнительных распределений , подобных этому . По сравнению с измененной версией для бенчмаркинга (10k элементов, размер партии 3, только подсчет), похоже, что сокращение использования памяти с 640 - & gt; 430 kb в соответствии со статистикой скрипки (слабая метрика, по общему признанию). – infogulch 8 April 2018 в 21:28
public static class MyExtensions
{
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items,
                                                       int maxItems)
    {
        return items.Select((item, inx) => new { item, inx })
                    .GroupBy(x => x.inx / maxItems)
                    .Select(g => g.Select(x => x.item));
    }
}

, и использование будет:

List<int> list = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

foreach(var batch in list.Batch(3))
{
    Console.WriteLine(String.Join(",",batch));
}

OUTPUT:

0,1,2
3,4,5
6,7,8
9
57
ответ дан L.B 16 August 2018 в 14:13
поделиться
  • 1
    Работал идеально для меня – NotForFun 6 August 2015 в 08:55
  • 2
    Написано здесь stackoverflow.com/a/31851776/288865 для полного примера реализации – NotForFun 6 August 2015 в 09:25
  • 3
    Как только GroupBy начинает перечисление, ему не нужно полностью перечислить его источник? Это теряет ленивую оценку источника и, следовательно, в некоторых случаях все преимущества дозирования! – ErikE 27 October 2015 в 19:37
  • 4
    Вау, спасибо, ты спас меня от безумия. Очень хорошо работает – riaandelange 3 February 2016 в 08:14
  • 5
    Как упоминает @ErikE, этот метод полностью перечисляет его источник, поэтому, хотя он выглядит хорошо, он побеждает цель ленивой оценки / конвейерной обработки – lasseschou 27 April 2016 в 20:08

Я написал пользовательскую реализацию IEnumerable, которая работает без linq и гарантирует единственное перечисление над данными. Он также выполняет все это, не требуя резервных списков или массивов, которые вызывают взрывы памяти на больших наборах данных.

Вот некоторые базовые тесты:

    [Fact]
    public void ShouldPartition()
    {
        var ints = new List<int> {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
        var data = ints.PartitionByMaxGroupSize(3);
        data.Count().Should().Be(4);

        data.Skip(0).First().Count().Should().Be(3);
        data.Skip(0).First().ToList()[0].Should().Be(0);
        data.Skip(0).First().ToList()[1].Should().Be(1);
        data.Skip(0).First().ToList()[2].Should().Be(2);

        data.Skip(1).First().Count().Should().Be(3);
        data.Skip(1).First().ToList()[0].Should().Be(3);
        data.Skip(1).First().ToList()[1].Should().Be(4);
        data.Skip(1).First().ToList()[2].Should().Be(5);

        data.Skip(2).First().Count().Should().Be(3);
        data.Skip(2).First().ToList()[0].Should().Be(6);
        data.Skip(2).First().ToList()[1].Should().Be(7);
        data.Skip(2).First().ToList()[2].Should().Be(8);

        data.Skip(3).First().Count().Should().Be(1);
        data.Skip(3).First().ToList()[0].Should().Be(9);
    }

Метод расширения для разделения данных .

/// <summary>
/// A set of extension methods for <see cref="IEnumerable{T}"/>. 
/// </summary>
public static class EnumerableExtender
{
    /// <summary>
    /// Splits an enumerable into chucks, by a maximum group size.
    /// </summary>
    /// <param name="source">The source to split</param>
    /// <param name="maxSize">The maximum number of items per group.</param>
    /// <typeparam name="T">The type of item to split</typeparam>
    /// <returns>A list of lists of the original items.</returns>
    public static IEnumerable<IEnumerable<T>> PartitionByMaxGroupSize<T>(this IEnumerable<T> source, int maxSize)
    {
        return new SplittingEnumerable<T>(source, maxSize);
    }
}

Это реализующий класс

    using System.Collections;
    using System.Collections.Generic;

    internal class SplittingEnumerable<T> : IEnumerable<IEnumerable<T>>
    {
        private readonly IEnumerable<T> backing;
        private readonly int maxSize;
        private bool hasCurrent;
        private T lastItem;

        public SplittingEnumerable(IEnumerable<T> backing, int maxSize)
        {
            this.backing = backing;
            this.maxSize = maxSize;
        }

        public IEnumerator<IEnumerable<T>> GetEnumerator()
        {
            return new Enumerator(this, this.backing.GetEnumerator());
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return this.GetEnumerator();
        }

        private class Enumerator : IEnumerator<IEnumerable<T>>
        {
            private readonly SplittingEnumerable<T> parent;
            private readonly IEnumerator<T> backingEnumerator;
            private NextEnumerable current;

            public Enumerator(SplittingEnumerable<T> parent, IEnumerator<T> backingEnumerator)
            {
                this.parent = parent;
                this.backingEnumerator = backingEnumerator;
                this.parent.hasCurrent = this.backingEnumerator.MoveNext();
                if (this.parent.hasCurrent)
                {
                    this.parent.lastItem = this.backingEnumerator.Current;
                }
            }

            public bool MoveNext()
            {
                if (this.current == null)
                {
                    this.current = new NextEnumerable(this.parent, this.backingEnumerator);
                    return true;
                }
                else
                {
                    if (!this.current.IsComplete)
                    {
                        using (var enumerator = this.current.GetEnumerator())
                        {
                            while (enumerator.MoveNext())
                            {
                            }
                        }
                    }
                }

                if (!this.parent.hasCurrent)
                {
                    return false;
                }

                this.current = new NextEnumerable(this.parent, this.backingEnumerator);
                return true;
            }

            public void Reset()
            {
                throw new System.NotImplementedException();
            }

            public IEnumerable<T> Current
            {
                get { return this.current; }
            }

            object IEnumerator.Current
            {
                get { return this.Current; }
            }

            public void Dispose()
            {
            }
        }

        private class NextEnumerable : IEnumerable<T>
        {
            private readonly SplittingEnumerable<T> splitter;
            private readonly IEnumerator<T> backingEnumerator;
            private int currentSize;

            public NextEnumerable(SplittingEnumerable<T> splitter, IEnumerator<T> backingEnumerator)
            {
                this.splitter = splitter;
                this.backingEnumerator = backingEnumerator;
            }

            public bool IsComplete { get; private set; }

            public IEnumerator<T> GetEnumerator()
            {
                return new NextEnumerator(this.splitter, this, this.backingEnumerator);
            }

            IEnumerator IEnumerable.GetEnumerator()
            {
                return this.GetEnumerator();
            }

            private class NextEnumerator : IEnumerator<T>
            {
                private readonly SplittingEnumerable<T> splitter;
                private readonly NextEnumerable parent;
                private readonly IEnumerator<T> enumerator;
                private T currentItem;

                public NextEnumerator(SplittingEnumerable<T> splitter, NextEnumerable parent, IEnumerator<T> enumerator)
                {
                    this.splitter = splitter;
                    this.parent = parent;
                    this.enumerator = enumerator;
                }

                public bool MoveNext()
                {
                    this.parent.currentSize += 1;
                    this.currentItem = this.splitter.lastItem;
                    var hasCcurent = this.splitter.hasCurrent;

                    this.parent.IsComplete = this.parent.currentSize > this.splitter.maxSize;

                    if (this.parent.IsComplete)
                    {
                        return false;
                    }

                    if (hasCcurent)
                    {
                        var result = this.enumerator.MoveNext();

                        this.splitter.lastItem = this.enumerator.Current;
                        this.splitter.hasCurrent = result;
                    }

                    return hasCcurent;
                }

                public void Reset()
                {
                    throw new System.NotImplementedException();
                }

                public T Current
                {
                    get { return this.currentItem; }
                }

                object IEnumerator.Current
                {
                    get { return this.Current; }
                }

                public void Dispose()
                {
                }
            }
        }
    }
0
ответ дан leat 16 August 2018 в 14:13
поделиться

Если вы начинаете с sequence, определенного как IEnumerable<T>, и знаете, что его можно безопасно перечислить несколько раз (например, потому что это массив или список), вы можете просто использовать этот простой шаблон для обработки элементы в партиях:

while (sequence.Any())
{
    var batch = sequence.Take(10);
    sequence = sequence.Skip(10);

    // do whatever you need to do with each batch here
}
17
ответ дан Matthew Strawbridge 16 August 2018 в 14:13
поделиться
  • 1
    Хороший, простой способ для пакетной обработки без кода или необходимости для внешней библиотеки – DevHawk 6 January 2017 в 20:40
  • 2
    @DevHawk: это так. Обратите внимание, однако, что производительность будет экспоненциально в больших (r) коллекциях. – RobIII 29 January 2018 в 11:07

Итак, с функциональной шляпой, это выглядит тривиально .... но в C # есть некоторые существенные недостатки.

вы, вероятно, рассмотрите это как разворот IEnumerable (google it and you Вероятно, в некоторых документах Haskell, возможно, есть некоторые вещи F #, если вы знаете F #, косоглазие в документах Haskell, и это будет иметь смысл).

Unfold относится к fold ( «aggregate»), за исключением повторения через входной IEnumerable, он выполняет итерацию через выходные структуры данных (это аналогичная связь между IEnumerable и IObservable, на самом деле я думаю, что IObservable реализует «разворачивающийся», называемый generate ...)

в любом случае сначала вам нужен метод разворачивания, я думаю, что это работает;

    static IEnumerable<T> Unfold<T, U>(Func<U, IEnumerable<Tuple<U, T>>> f, U seed)
    {
        var maybeNewSeedAndElement = f(seed);

        return maybeNewSeedAndElement.SelectMany(x => new[] { x.Item2 }.Concat(Unfold(f, x.Item1)));
    }

это немного тупо, потому что C # не реализует некоторые из функций, которые функциональные langauges принимают как должное ... но он в основном принимает семя, а затем генерирует ответ «Maybe» следующего элемента в IEnumerable и следующем семестре (возможно, не существует в C #, поэтому мы использовали IEnume (я не могу ругаться, чтобы подделать его), и конкатенирует остальную часть ответа (я не могу ручаться за сложность этого «O (n?)».

Как только вы это сделали,

    static IEnumerable<IEnumerable<T>> Batch<T>(IEnumerable<T> xs, int n)
    {
        return Unfold(ys =>
            {
                var head = ys.Take(n);
                var tail = ys.Skip(n);
                return head.Take(1).Select(_ => Tuple.Create(tail, head));
            },
            xs);
    }

все выглядит довольно чистым ... вы берете «n» элементы как «следующий» элемент в IEnumerable, а «tail» - это остальная часть необработанного списка.

, если в голове ничего нет ... вы закончили ... вы возвращаете «Nothing» (но поддельно как пустой IEnumerable>) ... иначе вы возвращаете элемент головы и хвост для обработки.

вы, вероятно, можете сделать это с помощью IObservable, вероятно, существует уже такой «пакетный» метод, и вы, вероятно, можете его использовать.

Если риск переполнения стека вызывает беспокойство (вероятно, он должен ), тогда вы должны реализовать в F # (и, вероятно, уже есть такая библиотека F # (FSharpX?)).

(Я только сделал некоторые рудиментарные тесты этого, так что могут быть нечетные ошибки в там).

1
ответ дан Mr D 16 August 2018 в 14:13
поделиться
    static IEnumerable<IEnumerable<T>> TakeBatch<T>(IEnumerable<T> ts,int batchSize)
    {
        return from @group in ts.Select((x, i) => new { x, i }).ToLookup(xi => xi.i / batchSize)
               select @group.Select(xi => xi.x);
    }
-2
ответ дан nichom 16 August 2018 в 14:13
поделиться
  • 1
    Добавьте описание или текст в свой ответ. Помещение только кода может означать меньше времени. – Ariful Haque 2 July 2015 в 16:52

Все вышеизложенное работает ужасно с большими партиями или небольшим объемом памяти. Пришлось написать свой собственный, который будет конвейер (нигде не заметил накопления элемента):

public static class BatchLinq {
    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size) {
        if (size <= 0)
            throw new ArgumentOutOfRangeException("size", "Must be greater than zero.");

        using (IEnumerator<T> enumerator = source.GetEnumerator())
            while (enumerator.MoveNext())
                yield return TakeIEnumerator(enumerator, size);
    }

    private static IEnumerable<T> TakeIEnumerator<T>(IEnumerator<T> source, int size) {
        int i = 0;
        do
            yield return source.Current;
        while (++i < size && source.MoveNext());
    }
}

Изменить: Известная проблема с этим подходом заключается в том, что каждая партия должна быть перечислина и перечислина полностью, прежде чем перейти к следующей партии. Например, это не работает:

//Select first item of every 100 items
Batch(list, 100).Select(b => b.First())
22
ответ дан Nick Whaley 16 August 2018 в 14:13
поделиться
  • 1
    Обычная @ L.B, выложенная выше, также не выполняет накопления элементов. – neontapir 15 July 2013 в 16:47
  • 2
    @neontapir Это так. GroupBy внутренне будет накапливаться. – Nick Whaley 15 July 2013 в 19:09
  • 3
    @neontapir Тем не менее. Машина для сортировки монет, которая сначала дает вам никель, а затем десять центов, должна сначала проверить каждую монету, прежде чем дать вам десять центов, чтобы убедиться, что больше нет никелей. – Nick Whaley 23 July 2013 в 15:11
  • 4
    Ahhh ahha, пропустил вашу заметку о редактировании, когда я зацепил этот код. Потребовалось некоторое время, чтобы понять, почему итерация по неперечисленным партиям фактически перечисляла всю оригинальную коллекцию (!!!), предоставляя пакеты X, каждый из которых перечислил 1 элемент (где X - количество оригинальных элементов коллекции). – eli 17 February 2014 в 12:03
  • 5
    @NickWhaley, если я делаю Count () в IEnumerable & lt; IEnumerable & lt; T & gt; gt; результат по вашему коду, он дает неправильный ответ, он дает общее количество элементов, когда ожидается общее количество созданных партий. Это не относится к MoreLinq Batch code – Mrinal Kamboj 26 March 2017 в 14:28

Я присоединяюсь к этому очень поздно, но нашел что-то более интересное.

Таким образом, мы можем использовать здесь Skip и Take для лучшей производительности.

public static class MyExtensions
    {
        public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> items, int maxItems)
        {
            return items.Select((item, index) => new { item, index })
                        .GroupBy(x => x.index / maxItems)
                        .Select(g => g.Select(x => x.item));
        }

        public static IEnumerable<T> Batch2<T>(this IEnumerable<T> items, int skip, int take)
        {
            return items.Skip(skip).Take(take);
        }

    }

Далее я проверил 100000 записей. Зацикливание занимает больше времени в случае Batch

Code of console application.

static void Main(string[] args)
{
    List<string> Ids = GetData("First");
    List<string> Ids2 = GetData("tsriF");

    Stopwatch FirstWatch = new Stopwatch();
    FirstWatch.Start();
    foreach (var batch in Ids2.Batch(5000))
    {
        // Console.WriteLine("Batch Ouput:= " + string.Join(",", batch));
    }
    FirstWatch.Stop();
    Console.WriteLine("Done Processing time taken:= "+ FirstWatch.Elapsed.ToString());


    Stopwatch Second = new Stopwatch();

    Second.Start();
    int Length = Ids2.Count;
    int StartIndex = 0;
    int BatchSize = 5000;
    while (Length > 0)
    {
        var SecBatch = Ids2.Batch2(StartIndex, BatchSize);
        // Console.WriteLine("Second Batch Ouput:= " + string.Join(",", SecBatch));
        Length = Length - BatchSize;
        StartIndex += BatchSize;
    }

    Second.Stop();
    Console.WriteLine("Done Processing time taken Second:= " + Second.Elapsed.ToString());
    Console.ReadKey();
}

static List<string> GetData(string name)
{
    List<string> Data = new List<string>();
    for (int i = 0; i < 100000; i++)
    {
        Data.Add(string.Format("{0} {1}", name, i.ToString()));
    }

    return Data;
}

Записанное время Это как.

Сначала - 00: 00: 00.0708, 00: 00: 00.0660

Второе (выберите и пропустите) - 00: 00: 00.0008, 00: 00: 00.0008

2
ответ дан Unknown User 16 August 2018 в 14:13
поделиться
  • 1
    GroupBy полностью перечисляет до того, как он произведет одну строку. Это не очень хороший способ сделать дозирование. – ErikE 27 April 2016 в 20:43
  • 2
    @ErikE Это зависит от того, чего вы пытаетесь достичь. Если пакетная обработка не является проблемой, и вам просто нужно разделить элементы на более мелкие куски для обработки, это может быть просто вещь. Я использую это для MSCRM, где может быть 100 записей, которые не являются проблемой для LAMBDA для пакетной работы .. его сохранение, которое занимает секунды .. – JensB 27 January 2017 в 13:02
  • 3
    Конечно, есть случаи, когда полное перечисление не имеет значения. Но зачем писать метод утилиты второго класса, когда вы можете написать превосходный? – ErikE 27 January 2017 в 17:15
  • 4
    Хорошая альтернатива, но не идентичная, как первая, возвращает список списков, позволяющих вам пройти. – Gareth Hopkins 26 January 2018 в 20:08

Такой же подход, как MoreLINQ, но с использованием List вместо Array. Я не проводил бенчмаркинг, но читаемость важна для некоторых людей:

    public static IEnumerable<IEnumerable<T>> Batch<T>(this IEnumerable<T> source, int size)
    {
        List<T> batch = new List<T>();

        foreach (var item in source)
        {
            batch.Add(item);

            if (batch.Count >= size)
            {
                yield return batch;
                batch.Clear();
            }
        }

        if (batch.Count > 0)
        {
            yield return batch;
        }
    }
1
ответ дан user4698855 16 August 2018 в 14:13
поделиться
  • 1
    Вы НЕ должны повторно использовать переменную партии. Ваши потребители могут быть полностью втянуты в это. Кроме того, перейдите в параметр size к вашему new List, чтобы оптимизировать его размер. – ErikE 26 September 2017 в 00:58
  • 2
    Простое исправление: замените batch.Clear(); на batch = new List<T>(); – NetMage 17 November 2017 в 21:57
2
ответ дан Johni Michels 29 October 2018 в 12:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: