yield
2017年1月9日 星期一
yield,它可以讓程式員以傳回每個元素的方式來自動產生IEnumerable<T>物件
例如下列程式會回傳1-5 五個數字的IEnumerable<T>物件:
這是一般的寫法,因為List<T>有實作IEnumerable<T>,所以很自然的會用List<T>作為產製IEnumerable<T>的寫法。
static IEnumerable<int> GetCollection1() { List<int> list = new List<int>(); for (int i = 1; i <= 5; i++) { list.Add(i); } return list; }
改為yield寫法,看起來更為精簡,yield指令會告訴編譯器,這一段函式的回傳值IEnumerable<T>內的元素由yield return所回傳的物件來填充,因此可省下額外使用集合物件事先封裝的工作。
static IEnumerable<int> GetCollection2() { for (int i = 1; i <= 5; i++) { yield return i; } }
比較二個方式的效能
private static int testMax = 5000000; static void Main(string[] args) { Stopwatch sw = new Stopwatch(); sw.Start(); var result = GetCollection1(); sw.Stop(); Console.WriteLine("Collection1:{0},count:{1}", sw.ElapsedMilliseconds, result.Count()); sw.Restart(); var result2 = GetCollection2(); sw.Stop(); Console.WriteLine("Collection2:{0},count:{1}", sw.ElapsedMilliseconds, result2.Count()); sw.Restart(); var result4 = GetCollection4(); sw.Stop(); Console.WriteLine("Collection4:{0},count:{1}", sw.ElapsedMilliseconds, result4.Count()); sw.Restart(); var result5 = GetCollection5(); sw.Stop(); Console.WriteLine("Collection5:{0},count:{1}", sw.ElapsedMilliseconds, result5.Count()); } static IEnumerable<int> GetCollection1() { List<int> list = new List<int>(); for (int i = 1; i <= testMax; i++) { list.Add(i); } return list; } static IEnumerable<int> GetCollection2() { for (int i = 1; i <= testMax; i++) { yield return i; } } static IEnumerable<Student> GetCollection4() { for (int i = 1; i <= testMax; i++) { yield return new Student() { Id = i, Name = i.ToString() }; } } static IEnumerable<Student> GetCollection5() { List<Student> studentList = new List<Student>(); for (int i = 1; i <= testMax; i++) { studentList.Add(new Student() {Id=i,Name=i.ToString() }); } return studentList; } public class Student { public int Id { get; set; } public string Name { get; set; } }
執行結果,我們發現使用yield寫法,所執行時間為0,這是因為延遲查詢,當我們對這個集合查詢時才會真正載入資料。
//查詢階段才會真正載入資料 Console.WriteLine(result4.Where(o => o.Id == 700).Select(o => o.Name).FirstOrDefault());
若是在foreach這類迭代運算中要中斷執行,則可利用yield break來中斷,如下程式碼:
static IEnumerable<int> GetCollection3(IEnumerable<int> NumberSeries) { foreach (var number in NumberSeries) { if (number > 10) { yield break; } else { yield return number; } } }
執行結果
Read more...