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,這是因為延遲查詢,當我們對這個集合查詢時才會真正載入資料。

image

 

//查詢階段才會真正載入資料
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;
            }
        }
    }

執行結果

image

0 意見: