Is there ever a reason to not use 'yield return' when returning an IEnumerable?
简单示例-您有一个方法或属性返回IEnumerable,调用方正在foreach()循环中迭代该方法或属性。在IEnumerable方法中应该始终使用"yield return"吗?有没有理由不这么做?虽然我知道这可能并不总是必要的,甚至"更好"(例如,它可能是一个非常小的集合),但有没有理由积极避免这样做?
让我想到这一点的代码是我写的一个函数,它非常类似于这个线程中可接受的答案——我如何循环访问一个日期范围?
迭代器块每次迭代时都执行"实时"评估。
然而,有时,您想要的行为是让结果在某个时间点成为一个"快照"。在这些情况下,您可能不想使用
如果直接处理查询对象,也不需要使用
1 2 3 4 5 6 7 8 | 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... |
不使用枚举器的一个明显原因是当需要
迭代器是非常好的,但是它们不能逃避"没有免费午餐"的原则。在.NET框架集合代码中找不到它们。这是一个很好的理由,它们不能像专用的实现那样高效。既然这对.NET设计者很重要,他们就无法预测效率何时重要。您可以,您知道您的代码是否处于程序的关键路径中。
迭代器的速度比专用实现慢一倍多。至少这是我通过测试
我将包含测试代码,以便您自己验证:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | 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(); } } |
奇怪的问题。如果您的方法返回的是来自其他地方的
我不这么认为。正如@lbushkin建议的那样,如果您打算整体返回某个东西,那么您将返回一个IList或其他什么东西。如果您返回的是IEnumerable,那么人们希望延迟执行,因此我认为在这种情况下应该始终使用yield。