返回IEnumerable时是否有理由不使用'yield return'?

Jer*_*ins 22 c# ienumerable yield-return

简单的例子 - 你有一个返回IEnumerable的方法或属性,调用者在foreach()循环中迭代它.你应该总是在你的IEnumerable方法中使用'yield return'吗?有没有理由不去?虽然我明白它可能并不总是有必要,甚至"更好"(例如,它可能是一个非常小的集合),但有没有理由主动避免这样做?

让我思考这个问题的代码是我写的一个函数非常类似于这个帖子中接受的答案 - 我如何遍历日期范围?

LBu*_*kin 23

迭代器块在每次迭代时执行"实时"评估.

但是,有时候,您想要的行为是将结果作为某个时间点的"快照".在这些情况下,您可能不想使用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...
Run Code Online (Sandbox Code Playgroud)


Han*_*ant 8

不使用枚举器的一个明显原因是您需要IEnumerator<>.Reset()工作时.

迭代器非常好,但他们无法摆脱"没有免费午餐"的原则.您将不会在.NET框架集合代码中找到它们.这是有充分理由的,它们不能像专用实现那样高效.现在这对.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(); }
}
Run Code Online (Sandbox Code Playgroud)


mqp*_*mqp 5

奇怪的问题。如果您的方法返回IEnumerable它从其他地方获取的 ,那么显然它不会使用yield return. 如果您的方法需要组装一个表示结果的具体数据结构,以便在返回之前对其进行一些操作,那么我想您也不会yield return在那里使用。