【问题标题】:Is there ever a reason to not use 'yield return' when returning an IEnumerable?返回 IEnumerable 时是否有理由不使用“yield return”?
【发布时间】:2011-04-20 20:59:55
【问题描述】:

简单示例 - 您有一个返回 IEnumerable 的方法或属性,并且调用者在 foreach() 循环中对其进行迭代。您是否应该始终在您的 IEnumerable 方法中使用“yield return”?有没有理由不这样做?虽然我知道它可能并不总是必要,甚至“更好”(例如,它可能是一个非常小的集合),但有没有理由积极避免这样做?

让我想到这一点的代码是我编写的一个函数,与该线程中接受的答案非常相似 - How do I loop through a date range?

【问题讨论】:

  • 如果你直接枚举一个预先存在的集合,你会增加大量的开销来获得一个小的绝缘好处(客户端代码不能转换结果来获取你的私人集合。)这通常是可以接受以提高可维护性,尽管在这种情况下它实际上会稍微增加代码复杂性。
  • Duplicate stackoverflow.com/questions/3969963/… 有不同的,可以说是更好的答案

标签: c# ienumerable yield-return


【解决方案1】:

迭代器块在每次迭代时都会执行“实时”评估。

但是,有时您想要的行为是让结果成为某个时间点的“快照”。在这些情况下,您可能不想使用yield return,而是返回List<>Set,或其他一些持久性集合。

如果您直接处理查询对象,也没有必要使用yield return。 LINQ 查询经常出现这种情况——最好只从查询中返回IEnumerable<>,而不是自己迭代和yield returning 结果。例如:

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&lt;&gt;.Reset() 工作时。

    迭代器非常很好,但它们无法逃脱“没有免费午餐”的原则。您不会在 .NET 框架集合代码中找到它们。这是有充分理由的,它们不能像专用实现那样高效。现在这对 .NET 设计人员很重要,但他们无法预测效率何时重要。你可以,你知道你的代码是否在你程序的关键路径中。

    迭代器的速度是专用实现的两倍多。至少这是我通过测试List&lt;&gt; 迭代器测量的结果。注意微优化,它们仍然非常快,而且它们的大哦是一样的。

    我将包含测试代码,以便您自己验证:

    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(); }
    }
    

    【讨论】:

    • 我将foreach (var item in lst) ; 更改为foreach (var item in lst) item.ToString(); 结果从7 27 更改为160 27 Fiddle。访问列表中的项目时出现问题。
    【解决方案3】:

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

    【讨论】:

      【解决方案4】:

      我不这么认为。正如@LBushkin 建议的那样,如果你要返回一个整体,你会返回一个 IList 或其他什么。如果你返回一个 IEnumerable,人们期望延迟执行,所以我认为你应该总是在这种情况下使用 yield。

      【讨论】:

        【解决方案5】:

        如果不阅读答案,我会告诉你,在 WCF 服务中使用 yield 会弄乱你的 FaultException!

        我花了很长时间才发现产量是罪魁祸首。我只需要一种快速的方法来运行一些错误。正确的方法当然是在回复中包含一个错误对象,但我需要它快。 我做了: throw new FaultException("没有 id 订阅:" + subscriptionId.ToString());

        但是使用yield,即使低于实际抛出,也会导致常规异常,网络

        【讨论】:

          猜你喜欢
          • 2011-01-04
          • 2010-11-07
          • 2011-03-26
          • 2012-04-09
          • 2011-12-09
          • 2012-01-03
          • 1970-01-01
          • 2011-01-28
          • 1970-01-01
          相关资源
          最近更新 更多