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

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

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

Ответ 1

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

Иногда, однако, поведение, которое вы хотите, - это результат "моментального снимка" в определенный момент времени. В этих случаях вы, вероятно, не хотите использовать 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...

Ответ 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(); }
}

Ответ 3

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

Ответ 4

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

Ответ 5

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

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

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