Nano Hash - криптовалюты, майнинг, программирование

Есть ли причина не использовать yield return при возврате IEnumerable?

Простой пример - у вас есть метод или свойство, которое возвращает IEnumerable, и вызывающий объект выполняет итерацию по нему в цикле foreach (). Следует ли всегда использовать yield return в своем методе IEnumerable? Есть ли причина не делать этого? Хотя я понимаю, что это может быть не всегда необходимо или даже «лучше» (например, может быть, это очень небольшая коллекция), есть ли причина активно избегать этого?

Часть кода, которая заставила меня задуматься об этом, была написанной мной функцией, очень похожей на принятый ответ в этом потоке - Как перебрать диапазон дат?


  • Если вы напрямую перечисляете уже существующую коллекцию, вы добавляете значительные накладные расходы для небольшого преимущества изоляции (клиентский код не может привести результат к вашей частной коллекции). Это часто приемлемо для улучшения ремонтопригодности, хотя в данном случае случае это на самом деле немного увеличивает сложность кода. 04.10.2010
  • Повторяющийся stackoverflow.com/questions/3969963/ имеет другие и, возможно, лучшие ответы 03.04.2017

Ответы:


1

Блоки итераторов выполняют "живую" оценку каждый раз при повторении.

Иногда, однако, вы хотите, чтобы результаты были «моментальным снимком» в определенный момент времени. В этих случаях вы, вероятно, не захотите использовать yield return, а вместо этого вернете List<> или Set или какую-нибудь другую постоянную коллекцию.

Также нет необходимости использовать yield return, если вы имеете дело с объектами запроса напрямую. Это часто случается с запросами LINQ - лучше просто вернуть IEnumerable<> из запроса, а не выполнять итерацию и yield return анализ результатов самостоятельно. Например:

var result = from obj in someCollection
             where obj.Value < someValue
             select new { obj.Name, obj.Value };

foreach( var item in result )
   yield return item; // THIS IS UNNECESSARY....

// just return {result} instead...
04.10.2010

2

Одна очевидная причина не использовать счетчик - это когда вам нужно IEnumerator<>.Reset() для работы.

Итераторы очень хороши, но они не могут избежать принципа «бесплатного обеда нет». Вы не найдете их используемых в коде коллекции .NET framework. Для этого есть веская причина, они не могут быть такими же эффективными, как специализированная реализация. Теперь, когда проектировщики .NET имели значение, они не могли предсказать, когда важна эффективность. Вы можете знать, находится ли ваш код на критическом пути вашей программы.

Итераторы чуть более чем в два раза медленнее, чем специализированная реализация. По крайней мере, это то, что я измерил, тестируя List<> итератор. Следите за микрооптимизациями, они по-прежнему очень быстрые, и их большое О остается таким же.

Я включу тестовый код, чтобы вы могли убедиться в этом сами:

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

class Program {
    static void Main(string[] args) {
        var lst = new MyList<int>();
        for (int ix = 0; ix < 10000000; ++ix) lst.Add(ix);
        for (int test = 0; test < 20; ++test) {
            var sw1 = Stopwatch.StartNew();
            foreach (var item in lst) ;
            sw1.Stop();
            var sw2 = Stopwatch.StartNew();
            foreach (var item in lst.GetItems()) ;
            sw2.Stop();
            Console.WriteLine("{0} {1}", sw1.ElapsedMilliseconds, sw2.ElapsedMilliseconds);
        }
        Console.ReadLine();

    }
}

class MyList<T> : IList<T> {
    private List<T> lst = new List<T>();

    public IEnumerable<T> GetItems() {
        foreach (T item in lst)
            yield return item;
    }

    public int IndexOf(T item) { return lst.IndexOf(item); }
    public void Insert(int index, T item) { lst.Insert(index, item); }
    public void RemoveAt(int index) { lst.RemoveAt(index); }
    public T this[int index] {
        get { return lst[index]; }
        set { lst[index] = value; }
    }
    public void Add(T item) { lst.Add(item); }
    public void Clear() { lst.Clear(); }
    public bool Contains(T item) { return lst.Contains(item); }
    public void CopyTo(T[] array, int arrayIndex) { lst.CopyTo(array, arrayIndex); }
    public int Count { get { return lst.Count; } }
    public bool IsReadOnly { get { return ((IList<T>)lst).IsReadOnly; } }
    public bool Remove(T item) { return lst.Remove(item); }
    public IEnumerator<T> GetEnumerator() { return lst.GetEnumerator(); }
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
}
04.10.2010
  • Я изменил foreach (var item in lst) ; на foreach (var item in lst) item.ToString();, результат изменился с 7 27 на 160 27 Fiddle. проблема возникает при доступе к элементу в списке. 19.11.2018

  • 3

    Странный вопрос. Если ваш метод возвращает IEnumerable, полученный откуда-то еще, очевидно, что он не будет использовать yield return. Если вашему методу необходимо собрать конкретную структуру данных, представляющую результаты, чтобы выполнить какие-либо манипуляции с ней перед возвратом, то я думаю, вы также не будете использовать yield return там.

    04.10.2010

    4

    Я так не думаю. Как предлагает @LBushkin, если вы собираетесь вернуть что-то целиком, вы вернете IList или что-то еще. Если вы возвращаете IEnumerable, люди ожидают отложенного выполнения, поэтому я думаю, вам всегда следует использовать yield в этом случае.

    04.10.2010

    5

    Не читая ответов, я скажу вам, что использование yield в службе WCF испортит ваше исключение FaultException!

    Мне потребовалось некоторое время, чтобы выяснить, что виной всему урожай. Мне просто нужен был быстрый способ запустить некоторые ошибки. Правильный способ, конечно, - включить в ответ объект Error, но мне это нужно было быстро. Я сделал: выбросил новое исключение FaultException («Нет подписки с идентификатором:» + subscriptionId.ToString ());

    Но использование yield, даже ниже фактического выброса, вызвало регулярное исключение, сеть

    22.07.2019
    Новые материалы

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

    Как написать эффективное резюме
    Предложения по дизайну и макету, чтобы представить себя профессионально Вам не позвонили на собеседование после того, как вы несколько раз подали заявку на работу своей мечты? У вас может..

    Частный метод Python: улучшение инкапсуляции и безопасности
    Введение Python — универсальный и мощный язык программирования, известный своей простотой и удобством использования. Одной из ключевых особенностей, отличающих Python от других языков, является..

    Как я автоматизирую тестирование с помощью Jest
    Шутка для победы, когда дело касается автоматизации тестирования Одной очень важной частью разработки программного обеспечения является автоматизация тестирования, поскольку она создает..

    Работа с векторными символическими архитектурами, часть 4 (искусственный интеллект)
    Hyperseed: неконтролируемое обучение с векторными символическими архитектурами (arXiv) Автор: Евгений Осипов , Сачин Кахавала , Диланта Хапутантри , Тимал Кемпития , Дасвин Де Сильва ,..

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

    Обеспечение масштабируемости LLM: облачный анализ с помощью AWS Fargate и Copilot
    В динамичной области искусственного интеллекта все большее распространение получают модели больших языков (LLM). Они жизненно важны для различных приложений, таких как интеллектуальные..