亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

C#特性 迭代器(下) yield以及流的延遲計算

2020-01-24 02:13:20
字體:
來源:轉載
供稿:網友

從0遍歷到20(不包括20),輸出遍歷到的每個元素,并將大于2的所有數字放到一個IEnumerable<int>中返回

解答1:(我以前經常這樣做)

static IEnumerable<int> WithNoYield()    {      IList<int> list = new List<int>();      for (int i = 0; i < 20; i++)      {        Console.WriteLine(i.ToString());        if(i > 2)          list.Add(i);      }      return list;    }

解答2:(自從有了C# 2.0我們還可以這樣做)

static IEnumerable<int> WithYield()    {      for (int i = 0; i < 20; i++)      {        Console.WriteLine(i.ToString());        if(i > 2)          yield return i;      }    }

如果我用下面這樣的代碼測試,會得到怎樣的輸出?
測試1:

測試WithNoYield()

復制代碼 代碼如下:

static void Main()
        {
            WithNoYield();
            Console.ReadLine();
        }

測試WithYield()

復制代碼 代碼如下:

static void Main()
        {
            WithYield();
            Console.ReadLine();
        }

測試2:
測試WithNoYield()

復制代碼 代碼如下:

static void Main()
        {
            foreach (int i in WithNoYield())
            {
                Console.WriteLine(i.ToString());
            }
            Console.ReadLine();
        }

測試WithYield()

復制代碼 代碼如下:

static void Main()
        {
            foreach (int i in WithYield())
            {
                Console.WriteLine(i.ToString());
            }
            Console.ReadLine();
        }

給你5分鐘時間給出答案,不要上機運行

*********************************5分鐘后***************************************

測試1的運算結果
測試WithNoYield():輸出從0-19的數字
測試WithYield():什么都不輸出
測試2的運算結果
測試WithNoYield():輸出1-19接著輸出3-19
測試WithYield():輸出12334455…….
(為節省空間上面的答案沒有原樣粘貼,可以自己運行測試)

 

是不是感到很奇怪,為什么使用了yield的程序表現的如此怪異呢?

測試1中對WithYield()的測試,明明方法調用了,居然一行輸出都沒有,難道for循環根本沒有執行?通過斷點調試果然如此,for循環根本沒有進去,這是咋回事?測試2中對WithYield()的測試輸出是有了,不過輸出怎么這么有趣?穿插著輸出,在foreach遍歷WithYield()的結果的時候,好像不等到最后一條遍歷完,WithYield()不退出,這又是怎么回事?

還是打開IL代碼瞧一瞧到底發生了什么吧

Main方法的IL代碼:

.method private hidebysig static void Main() cil managed{  .entrypoint  .maxstack 1  .locals init (    [0] int32 i,    [1] class [mscorlib]System.Collections.Generic.IEnumerator`1<int32> CS$5$0000)  L_0000: call class [mscorlib]System.Collections.Generic.IEnumerable`1<int32> TestLambda.Program::WithYield()  L_0005: callvirt instance class [mscorlib]System.Collections.Generic.IEnumerator`1<!0> [mscorlib]System.Collections.Generic.IEnumerable`1<int32>::GetEnumerator()  L_000a: stloc.1   L_000b: br.s L_0020  L_000d: ldloc.1   L_000e: callvirt instance !0 [mscorlib]System.Collections.Generic.IEnumerator`1<int32>::get_Current()  L_0013: stloc.0   L_0014: ldloca.s i  L_0016: call instance string [mscorlib]System.Int32::ToString()  L_001b: call void [mscorlib]System.Console::WriteLine(string)  L_0020: ldloc.1   L_0021: callvirt instance bool [mscorlib]System.Collections.IEnumerator::MoveNext()  L_0026: brtrue.s L_000d  L_0028: leave.s L_0034  L_002a: ldloc.1   L_002b: brfalse.s L_0033  L_002d: ldloc.1   L_002e: callvirt instance void [mscorlib]System.IDisposable::Dispose()  L_0033: endfinally   L_0034: call string [mscorlib]System.Console::ReadLine()  L_0039: pop   L_003a: ret   .try L_000b to L_002a finally handler L_002a to L_0034}

這里沒什么稀奇的,在上一篇我已經分析過了,foreach內部就是轉換成調用迭代器的MoveNext()方法進行while循環。我瀏覽到WithYield()方法:

復制代碼 代碼如下:

private static IEnumerable<int> WithYield()
{
    return new <WithYield>d__0(-2);
}

暈,怎么搞的,這是我寫的代碼么?我的for循環呢?經過我再三確認,確實是我寫的代碼生成的。我心里暗暗叫罵,編譯器,你怎么能這樣“無恥”,在背后修改我的代碼,你這不侵權么。還給我新生成了一個類<WithYield>d__0,這個類實現了這么幾個接口:IEnumerable<int>, IEnumerable, IEnumerator<int>, IEnumerator, IDisposable(好啊,這個類將枚舉接口和迭代器接口都實現了)
現在能解答測試1為什么沒有輸出了,調用WithYield()里面就是調用了一下<WithYield>d__0的構造方法,<WithYield>d__0的構造方法的代碼:

復制代碼 代碼如下:

public <WithYield>d__0(int <>1__state)
    {
        this.<>1__state = <>1__state;
        this.<>l__initialThreadId = Thread.CurrentThread.ManagedThreadId;
    }

這里沒有任何輸出。
在測試2中,首先我們會調用<WithYield>d__0的GetEnumerator()方法,這個方法里將一個整型局部變量<>1__state初始化為0,再看看MoveNext()方法的代碼:

private bool MoveNext()  {    switch (this.<>1__state)    {      case 0:        this.<>1__state = -1;        this.<i>5__1 = 0;        goto Label_006A;      case 1:        this.<>1__state = -1;        goto Label_005C;      default:        goto Label_0074;    }  Label_005C:    this.<i>5__1++;  Label_006A:    if (this.<i>5__1 < 20)    {      Console.WriteLine(this.<i>5__1.ToString());      if (this.<i>5__1 > 2)      {        this.<>2__current = this.<i>5__1;        this.<>1__state = 1;        return true;      }      goto Label_005C;    }  Label_0074:    return false;  }

原來我們for循環里面的Console.WriteLine跑到這里來了,所以沒等到MoveNext()調用,for里面的輸出也是不會被執行的,因為每次遍歷都要訪問MoveNext()方法,所以沒有等到返回結果里面的元素遍歷完WithYield()也是不會退出的?,F在我們的測試程序所表現出來的怪異行為是可以找到依據了,那就是:編譯器在后臺搞了鬼。

實際上這種實現在理論上是有支撐的:延遲計算(Lazy evaluation或delayed evaluation)在Wiki上可以找到它的解釋:將計算延遲,直到需要這個計算的結果的時候才計算,這樣就可以因為避免一些不必要的計算而改進性能,在合成一些表達式時候還可以避免一些不必要的條件,因為這個時候其他計算都已經完成了,所有的條件都已經明確了,有的根本不可達的條件可以不用管了。反正就是好處很多了。

延遲計算來源自函數式編程,在函數式編程里,將函數作為參數來傳遞,你想呀,如果這個函數一傳遞就被計算了,那還搞什么搞,如果你使用了延遲計算,表達式在沒有使用的時候是不會被計算的,比如有這樣一個應用:x=expression,將這個表達式賦給x變量,但是如果x沒有在別的地方使用的話這個表達式是不會被計算的,在這之前x里裝的是這個表達式。

看來這個延遲計算真是個好東西,別擔心,整個Linq就是建立在這之上的,這個延遲計算可是幫了Linq的大忙啊(難道在2.0的時候,微軟就為它的Linq開始蓄謀了?),看下面的代碼:

var result = from book in books  where book.Title.StartWiths(“t”)  select bookif(state > 0){  foreach(var item in result)  {    //….}}

result是一個實現了IEnumerable<T>接口的類(在Linq里,所有實現了IEnumerable<T>接口的類都被稱作sequence),對它的foreach或者while的訪問必須通過它對應的IEnumerator<T>的MoveNext()方法,如果我們把一些耗時的或者需要延遲的操作放在MoveNext()里面,那么只有等到MoveNext()被訪問,也就是result被使用的時候那些操作才會執行,而給result賦值啊,傳遞啊,什么的,那些耗時的操作都沒有被執行。

如果上面這段代碼,最后由于state小于0,而對result沒有任何需求了,在Linq里返回的結果都是IEnumerable<T>的,如果這里沒有使用延遲計算,那那個Linq表達式不就白運算了么?如果是Linq to Objects還稍微好點,如果是Linq to SQL,而且那個數據庫表又很大,真是得不償失啊,所以微軟想到了這點,這里使用了延遲計算,只有等到程序別的地方使用了result才會計算這里的Linq表達式的值的,這樣Linq的性能也比以前提高了不少,而且Linq to SQL最后還是要生成SQL語句的,對于SQL語句的生成來說,如果將生成延遲,那么一些條件就先確定好了,生成SQL語句的時候就可以更精練了。還有,由于MoveNext()是一步步執行的,循環一次執行一次,所以如果有這種情況:我們遍歷一次判斷一下,不滿足我們的條件了我們就退出,如果有一萬個元素需要遍歷,當遍歷到第二個的時候就不滿足條件了,這個時候我們就可就此退出,后面那么多元素實際上都沒處理呢,那些元素也沒有被加載到內存中來。

延遲計算還有很多惟妙惟肖的特質,也許以后你也可以按照這種方式來編程了呢。寫到這里我突然想到了Command模式,Command模式將方法封裝成類,Command對象在傳遞等時候是不會執行任何東西的,只有調用它內部那個方法他才會執行,這樣我們就可以把命令到處發,還可以壓棧啊等等而不擔心在傳遞過程中Command被處理了,也許這也算是一種延遲計算吧。

本文也只是很淺的談了一下延遲計算的東西,從這里還可以牽扯到并發編程模型和協同程序等更多內容,由于本人才疏學淺,所以只能介紹到這個地步了,上面一些說法也是我個人理解,肯定有很多不妥地方,歡迎大家拍磚。

foreach,yield,這個我們平常經常使用的東西居然背后還隱藏著這么多奇妙的地方,我也是今天才知道,看來未來的路還很遠很遠啊。

路漫漫其修遠兮,吾將上下而求索。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美插天视频在线播放| 日韩欧美aaa| 中文字幕亚洲一区二区三区五十路| 色偷偷888欧美精品久久久| 国产精品福利久久久| 国产一区二区美女视频| 国产日韩欧美视频| 国产女人精品视频| 日韩电影免费在线观看| 欧美日韩在线另类| 热久久视久久精品18亚洲精品| 免费av一区二区| 国产一区二区日韩| 日韩亚洲欧美中文在线| 在线精品国产欧美| 亚洲第一页中文字幕| 91亚洲va在线va天堂va国| 日韩视频免费大全中文字幕| 久久精品人人爽| 久久影视电视剧凤归四时歌| 亚洲国产又黄又爽女人高潮的| 91国产高清在线| 日韩成人高清在线| 亚洲精品久久7777777| 久久激情五月丁香伊人| 欧美一区二区三区四区在线| 91成品人片a无限观看| 欧美激情久久久久| 久久人91精品久久久久久不卡| 77777少妇光屁股久久一区| 国产综合在线观看视频| 精品国产视频在线| 国产综合色香蕉精品| 久久九九精品99国产精品| 久久夜色精品国产欧美乱| 亚洲欧美第一页| 91九色蝌蚪国产| 精品久久久国产精品999| 精品美女久久久久久免费| 亚洲电影免费观看| 亚洲午夜色婷婷在线| 亚洲视频一区二区三区| 日韩av中文字幕在线| 亚洲免费视频一区二区| 影音先锋欧美精品| 日韩理论片久久| 中文字幕欧美日韩| 亚洲人成人99网站| 欧美激情视频一区二区三区不卡| 精品无人区太爽高潮在线播放| 亚洲福利小视频| 欧美性感美女h网站在线观看免费| 在线日韩中文字幕| 欧美电影免费观看高清完整| 高潮白浆女日韩av免费看| 亚洲国产精品99| 国产欧美日韩最新| 精品亚洲夜色av98在线观看| 欧美日本中文字幕| 国产精品视频免费观看www| 欧美成人精品h版在线观看| 亚洲区bt下载| 97色在线视频观看| 亚洲人av在线影院| 欧美国产视频日韩| 亚洲视屏在线播放| 国产精品美女久久久久av超清| 欧美另类在线播放| 亚洲美女久久久| 久久成人人人人精品欧| 久久久久久亚洲精品| 日本欧美精品在线| 成人黄色av免费在线观看| 日韩精品在线免费观看| 国产精品三级美女白浆呻吟| 91精品国产色综合久久不卡98口| 亚洲综合精品伊人久久| 中文字幕亚洲无线码a| 国产suv精品一区二区三区88区| 久久午夜a级毛片| 亚洲国产精品电影在线观看| 中文字幕在线看视频国产欧美| 91久久久久久久久| 最新国产成人av网站网址麻豆| 成人性生交xxxxx网站| 欧美福利视频在线观看| 亚洲аv电影天堂网| 2019亚洲男人天堂| 国产精品盗摄久久久| 亚洲激情小视频| 国产日韩欧美黄色| 国产美女久久久| www.国产一区| 51精品在线观看| 91大神在线播放精品| 亚洲精品国产品国语在线| 国产成人久久久精品一区| 性色av一区二区三区在线观看| 欧美日韩ab片| 亚洲国产精品人久久电影| 国产视频精品在线| 久久综合色88| 日韩中文在线观看| 久久久久久亚洲| 欧美xxxx18国产| 91精品国产高清久久久久久91| 在线视频欧美日韩精品| 欧美成年人视频网站| 亚洲激情中文字幕| 亚洲综合色激情五月| 尤物99国产成人精品视频| 国产精品久久久av久久久| 国产精品视频免费观看www| 日本午夜在线亚洲.国产| 91国内在线视频| 欧美在线欧美在线| 欧美日韩在线一区| 久久亚洲综合国产精品99麻豆精品福利| 亚洲国产精品字幕| 青青草国产精品一区二区| 色99之美女主播在线视频| 777777777亚洲妇女| 日韩精品久久久久久福利| 欧美激情xxxxx| 亚洲成人三级在线| 亚洲男人天堂手机在线| 日韩精品亚洲元码| 亚洲一二在线观看| 成人免费网站在线看| 色爱精品视频一区| 欧美精品一区二区三区国产精品| 久久影视电视剧凤归四时歌| 日韩中文字幕视频在线观看| 亚洲欧洲成视频免费观看| 亚洲二区在线播放视频| 久久久久99精品久久久久| 久久国产加勒比精品无码| 亚洲成人网久久久| 欧美精品久久久久久久久久| 欧美日本高清一区| 国产精品久久久久久久午夜| 久久久久五月天| 欧美精品在线观看| 热re99久久精品国产66热| 91精品久久久久久久久中文字幕| 中文字幕亚洲图片| 在线观看亚洲视频| 成人精品一区二区三区电影免费| 精品久久中文字幕久久av| 成人黄色激情网| www.日本久久久久com.| 亚洲一区av在线播放| 亚洲欧美日韩综合| 欧美成人午夜免费视在线看片| 亚洲欧美一区二区精品久久久| 成人国产精品日本在线| 国产中文字幕91| 亚洲开心激情网| 精品久久久精品| 久久精品国产亚洲精品| 欧美影院久久久| 国产成人激情小视频| 97在线免费视频| 国产精品嫩草视频|