Is there ever a reason to not use 'yield return' when returning an IEnumerable?

后端 未结 5 745
北海茫月
北海茫月 2020-12-09 16:16

Simple example - you have a method or a property that returns an IEnumerable and the caller is iterating over that in a foreach() loop. Should you always be using

5条回答
  •  没有蜡笔的小新
    2020-12-09 16:48

    One clear reason to not use an enumerator is when you need IEnumerator<>.Reset() to work.

    Iterators are very nice, but they can't escape the "there's no free lunch" principle. You won't find them used in the .NET framework collection code. There's a good reason for that, they can't be as efficient as a dedicated implementation. Now that mattered to the .NET designers, they couldn't predict when efficiency matters. You can, you know whether your code is in the critical path of your program.

    Iterators are a bit over twice as slow as a dedicated implementation. At least that's what I measured by testing the List<> iterator. Watch out for micro optimizations, they are still really fast and their big Oh is the same.

    I'll include the testing code so you can verify this for yourself:

    using System;
    using System.Collections.Generic;
    using System.Diagnostics;
    
    class Program {
        static void Main(string[] args) {
            var lst = new MyList();
            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 : IList {
        private List lst = new List();
    
        public IEnumerable 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)lst).IsReadOnly; } }
        public bool Remove(T item) { return lst.Remove(item); }
        public IEnumerator GetEnumerator() { return lst.GetEnumerator(); }
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return GetEnumerator(); }
    }
    

提交回复
热议问题