<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          C#中的閉包和意想不到的坑

          共 3486字,需瀏覽 7分鐘

           ·

          2020-08-04 04:03







          轉(zhuǎn)自:老胡寫(xiě)代碼
          cnblogs.com/deatharthas/p/13166987.html

          雖然閉包主要是函數(shù)式編程的玩意兒,而C#的最主要特征是面向?qū)ο螅抢梦谢騦ambda表達(dá)式,C#也可以寫(xiě)出具有函數(shù)式編程風(fēng)味的代碼。


          同樣的,使用委托或者lambda表達(dá)式,也可以在C#中使用閉包。


          根據(jù)WIKI的定義,閉包又稱語(yǔ)法閉包或函數(shù)閉包,是在函數(shù)式編程語(yǔ)言中實(shí)現(xiàn)語(yǔ)法綁定的一種技術(shù)。閉包在實(shí)現(xiàn)上是一個(gè)結(jié)構(gòu)體,它存儲(chǔ)了一個(gè)函數(shù)(通常是其入口地址)和一個(gè)關(guān)聯(lián)的環(huán)境(相當(dāng)于一個(gè)符號(hào)查找表)。閉包也可以延遲變量的生存周期。


          看定義好像有點(diǎn)迷糊,讓我們看看下面的例子


          class Program
          {
          static Action CreateGreeting(string message)
          {
          return () => { Console.WriteLine("Hello " + message); };
          }
          static void Main()
          {
          Action action = CreateGreeting("DeathArthas");
          action();
          }
          }


          這個(gè)例子非常簡(jiǎn)單,用lambda表達(dá)式創(chuàng)建一個(gè)Action對(duì)象,之后再調(diào)用這個(gè)Action對(duì)象。


          但是仔細(xì)觀察會(huì)發(fā)現(xiàn),當(dāng)Action對(duì)象被調(diào)用的時(shí)候,CreateGreeting方法已經(jīng)返回了,作為它的實(shí)參的message應(yīng)該已經(jīng)被銷毀了,那么為什么我們?cè)谡{(diào)用Action對(duì)象的時(shí)候,還是能夠得到正確的結(jié)果呢?


          原來(lái)奧秘就在于,這里形成了閉包。雖然CreateGreeting已經(jīng)返回了,但是它的局部變量被返回的lambda表達(dá)式所捕獲,延遲了其生命周期。怎么樣,這樣再回頭看閉包定義,是不是更清楚了一些?

           

          閉包就是這么簡(jiǎn)單,其實(shí)我們經(jīng)常都在使用,只是有時(shí)候我們都不自知而已。比如大家肯定都寫(xiě)過(guò)類似下面的代碼。


          void AddControlClickLogger(Control control, string message)
          {
          control.Click += delegate
          {
          Console.WriteLine("Control clicked: {0}", message);
          }
          }


          這里的代碼其實(shí)就用了閉包,因?yàn)槲覀兛梢钥隙ǎ赾ontrol被點(diǎn)擊的時(shí)候,這個(gè)message早就超過(guò)了它的聲明周期。合理使用閉包,可以確保我們寫(xiě)出在空間和時(shí)間上面解耦的委托。

           

          不過(guò)在使用閉包的時(shí)候,要注意一個(gè)陷阱。因?yàn)殚]包會(huì)延遲局部變量的生命周期,在某些情況下程序產(chǎn)生的結(jié)果會(huì)和預(yù)想的不一樣。讓我們看看下面的例子。

          class Program
          {
          static List CreateActions ()
          {
          var result = new List ();
          for( int i = 0; i < 5; i++)
          {
          result.Add(() => Console.WriteLine(i));
          }
          return result;
          }
          static void Main()
          {
          var actions = CreateActions();
          for( int i = 0;i
          {
          actions[i]();
          }
          }
          }


          這個(gè)例子也非常簡(jiǎn)單,創(chuàng)建一個(gè)Action鏈表并依次執(zhí)行它們。看看結(jié)果



          相信很多人看到這個(gè)結(jié)果的表情是這樣的!!難道不應(yīng)該是0,1,2,3,4嗎?出了什么問(wèn)題?


          刨根問(wèn)底,這兒的問(wèn)題還是出現(xiàn)在閉包的本質(zhì)上面,作為“閉包延遲了變量的生命周期”這個(gè)硬幣的另外一面,是一個(gè)變量可能在不經(jīng)意間被多個(gè)閉包所引用。


          在這個(gè)例子里面,局部變量i同時(shí)被5個(gè)閉包引用,這5個(gè)閉包共享i,所以最后他們打印出來(lái)的值是一樣的,都是i最后退出循環(huán)時(shí)候的值5。


          要想解決這個(gè)問(wèn)題也很簡(jiǎn)單,多聲明一個(gè)局部變量,讓各個(gè)閉包引用自己的局部變量就可以了。


          //其他都保持與之前一致
          static List CreateActions( )
          {
          var result = new List ();
          for ( int i = 0; i < 5; i++)
          {
          int temp = i; //添加局部變量
          result.Add(() => Console.WriteLine(temp));
          }
          return result;
          }



          這樣各個(gè)閉包引用不同的局部變量,剛剛的問(wèn)題就解決了。


          除此之外,還有一個(gè)修復(fù)的方法,在創(chuàng)建閉包的時(shí)候,使用foreach而不是for。至少在C# 7.0 的版本上面,這個(gè)問(wèn)題已經(jīng)被注意到了,使用foreach的時(shí)候編譯器會(huì)自動(dòng)生成代碼繞過(guò)這個(gè)閉包陷阱。


          //這樣fix也是可以的
          static List CreateActions()
          {
          var result = new List ();
          foreach ( var i in Enumerable.Range( 0, 5))
          {
          result.Add(() => Console.WriteLine(i));
          }
          return result;
          }


          這就是在閉包在C#中的使用和其使用中的一個(gè)小陷阱,希望大家能通過(guò)老胡的文章了解到這個(gè)知識(shí)點(diǎn)并且在開(kāi)發(fā)中少走彎路!


          往期推薦

          10大程序員必逛網(wǎng)站,良心推薦,建議收藏!
          MySQL 的這個(gè) BUG,坑了多少人?
          為什么需要了解ASP.NET Core MVC的前世今生
          最全C#自學(xué)資源匯總
          回復(fù) 【關(guān)閉】學(xué)關(guān)
          回復(fù) 【實(shí)戰(zhàn)】獲取20套實(shí)戰(zhàn)源碼
          回復(fù) 【福利】獲取最新微信支付有獎(jiǎng)勵(lì)
          回復(fù) 【被刪】學(xué)個(gè)
          回復(fù) 【訪客】學(xué)
          回復(fù) 【卡通】學(xué)制作微信卡通頭像
          回復(fù) 【python】學(xué)微獲取全套0基礎(chǔ)Python知識(shí)手冊(cè)
          回復(fù) 【2019】獲取2019 .NET 開(kāi)發(fā)者峰會(huì)資料PPT
          瀏覽 55
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  玖玖国产精品视频 | 亚洲国产日韩在线 | 日本一本在线观看视频干女人 | 日日于夜夜操免费视频 | 青草草视频精品视频免费观看 |