程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 初次使用C#中的yield,

初次使用C#中的yield,

編輯:C#入門知識

初次使用C#中的yield,


  這幾天在Python程序員的微信訂閱號中總是見到yield的關鍵字,才想起來在C#中也是有yield,但是只是知道有,從來沒有了解過他的用法,今天有時間就來看看是怎麼使用的。剛開始肯定就是搜索一下用法了,找到兩篇說明示例,一是 C# 中的"yield"使用,第二個是MSDN的官方api yield(C# 參考)

說實話第一個示例看完還是很模糊的概念,例子也沒有看懂是在干嘛,一直到MSDN中給出結果集我才明白了到底的用法是怎麼樣的。

先來舉例一個需求: 一個方法返回一個IEnumerable 類型結果集(例如返回一個list<int>的結果),通常的代碼是這樣的

 

 1         /// <summary>
 2         /// 
 3         /// </summary>
 4         /// <returns></returns>
 5         public IEnumerable<int> Method()
 6         {
 7             List<int> results = new List<int>();
 8             int counter = 0;
 9             int result = 1;
10 
11             while (counter++ < 10)
12             {
13                 result = result * 2;
14                 results.Add(result);
15             }
16             return results;
17         }

這樣就完成了需求。

但是 有了yield之後就可以這樣寫了

 1         /// <summary>
 2         /// 
 3         /// </summary>
 4         /// <returns></returns>
 5         public IEnumerable<int> YieldDemo()
 6         {
 7             int counter = 0;
 8             int result = 1;
 9             while (counter++ < 10)
10             {
11                 result = result * 2;              
12                 yield return result;
13             }
14         }

兩種效果是一樣的,但是從我個人而言我喜歡第二個,感覺更簡潔一些。

題外話:在寫這兩個例子中我又增加了一個知識點

  返回值IEnumerable其實和IEnumerable<object>是等價的,只是IEnumerable的結果需要動態的解析; 

 

但是還搞不清楚這兩者實現有什麼區別,所以我想看看這兩個在做同一件事的時候效率如何,下面來嘗試使用while循環10000000的取數據的耗時比較

使用 BenchmarkDotNet  測試結果如下

使用Stopwatch測試結果也是一樣的

從這個測試裡面可以看出YieldDemo方法幾乎沒有耗時,但是實際情況是不可能的吧,所以我又嘗試做了遍歷的測試

 

 1             Stopwatch stop = new Stopwatch();
 2             stop.Start();
 3             var res = new YieldTest().YieldDemo();
 4             foreach (var item in res)
 5             {
 6 
 7             }
 8             var a = stop.ElapsedMilliseconds;
 9             stop.Restart();
10 
11 
12             var rrrrr = new YieldTest().Method();
13             foreach (var item in rrrrr)
14             {
15 
16             }
17             var b = stop.ElapsedMilliseconds;
18             stop.Restart();

這個測試的結果是a=168,b=142.對比上一個測試結果讓我更加疑惑,我就開始打斷點,看看執行的順序是怎樣的。

結果如下:  

     在 第三行 斷點壓根就沒有進YieldDemo這個方法,而是當進行foreach 遍歷結果的時候,才開始進入了YieldDemo這個方法,更奇怪的是每次的foreach 都會進入YieldDemo的while一次去取數據

這個結果讓我有點懵了,只能再仔細看看文檔解析,

  迭代器方法運行到 yield return 語句時,會返回一個 expression,並保留當前在代碼中的位置。 下次調用迭代器函數時,將從該位置重新開始執行。 可以使用 yield break 語句來終止迭代。 

貌似這裡面是涉及到了迭代器的東西。馬上找迭代器的知識點,在 詳解C# 迭代器 中看到這樣一句解釋

  需要強調的一點是,對於迭代塊,雖然我們寫的方法看起來像是在順序執行,實際上我們是讓編譯器來為我們創建了一個狀態機。這就是在C#1中我們書寫的那部分代碼---調用者每次調用只需要返回一個值,因此我們需要記住最後一次返回值時,在集合中位置。

  當編譯器遇到迭代塊時,它創建了一個實現了狀態機的內部類。這個類記住了我們迭代器的准確當前位置以及本地變量,包括參數。

這句話貌似解析了上面的疑問,但是看的有點雲裡霧裡,還要花時間消化一下裡面的具體原理 。

官方提示使用yield有一些限制,需要注意

1 不能將 yield return 語句置於 try-catch 塊中。 可將 yield return 語句置於 try-finally 語句的 try 塊中。
2 可將 yield break 語句置於 try 塊或 catch 塊中,但不能將其置於 finally 塊中。
3 如果 foreach 主體(在迭代器方法之外)引發異常,則將執行迭代器方法中的 finally 塊。
4 匿名方法。 有關詳細信息,請參閱匿名方法。
5 包含不安全的塊的方法。 有關詳細信息,請參閱unsafe。

 

針對第一點讓我感覺使用好有限制,為啥不能在try-catch 中使用呢?

關於其他的一些使用方法在MSDN裡面都有詳細的講解,感覺沒有什麼好多說的。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved