成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

淺談C#閉包的相關(guān)原理

開發(fā) 后端
今天我們將談到的是C#與閉包,包括變量作用域和變量的生命周期。希望對(duì)大家有所幫助。

  首先想說明一點(diǎn),雖然有這樣那樣的不好的心態(tài)(比如中文技術(shù)書),但總體來說,國內(nèi)的技術(shù)人員還是喜歡分享和教導(dǎo)別人的,這點(diǎn)我的個(gè)人感受和之前在園子里看到的朋友的感受恰恰相反.我個(gè)人其實(shí)國內(nèi)很多技術(shù)網(wǎng)友都是很熱心的,可能因?yàn)檎Z言問題同一個(gè)技術(shù)熱點(diǎn)會(huì)稍稍落后國外一些,但一些成熟的或者基礎(chǔ)的概念都可以找到很細(xì)致的中文介紹,特別是關(guān)于閉包,因?yàn)樗淖置娼忉尨_實(shí)很繞,所以基本所有試圖解釋這一名詞的同學(xué)都是盡量用自己認(rèn)為最通俗易懂的方式來進(jìn)行講解.閑話扯遠(yuǎn)了,這里我就用C#語言來給大家解釋下閉包吧。

  其實(shí)要提到閉包,我們還得先提下變量作用域和變量的生命周期。

  在C#里面,變量作用域有三種,一種是屬于類的,我們常稱之為field,第二種則屬于函數(shù)的,我們通常稱之為局部變量,還有一種,其實(shí)也是屬于函數(shù)的,不過它的作用范圍更小,它只屬于函數(shù)局部的代碼片段,這種同樣稱之為局部變量。這三種變量的生命周期基本都可以用一句話來說明,每個(gè)變量都屬于它所寄存的對(duì)象,即變量隨著其寄存對(duì)象生而生和消亡.對(duì)應(yīng)三種作用域我們可以這樣說,類里面的變量是隨著類的實(shí)例化而生,同時(shí)伴隨著類對(duì)象的資源回收而消亡(當(dāng)然這里不包括非實(shí)例化的static和const對(duì)象).而函數(shù)(或代碼片段)的變量也隨著函數(shù)(或代碼片段)調(diào)用開始而生,伴隨函數(shù)(或代碼片段)調(diào)用結(jié)束而自動(dòng)由GC釋放,它內(nèi)部變量生命周期滿足先進(jìn)后出的特性。

  那么這里有沒有例外呢?

  答案是有的,不過在提這點(diǎn)之前,我還需要給各位另外一個(gè)名詞.都說c#就是MS版本的java,這話在.net 1.0可能可以這么說,但自2.0之后C#就可以自豪的說它絕非java了,這里面委托有很大的功勞,如果用過java和C#的人并且嘗試過寫winform程序時(shí)全部手寫實(shí)現(xiàn)代碼的人就會(huì)有這樣一個(gè)感受,同樣的click事件,在java中必須要無端的套個(gè)匿名類,但在c#中,你是可以直接將函數(shù)名+=到事件之后而不需要顯示寫上匿名委托的對(duì)象類型的,因?yàn)榫幾g器會(huì)幫你做這部分工作,在3.0和以后的版本之中,微軟將委托的用法更是發(fā)揮的淋漓精致,無論是簡潔的Lamda還是通俗易懂的LINQ,都是源自委托的.

  你可能要問,委托和我們今天要講的閉包又有什么關(guān)系呢?

  我們知道,c#,java和javascript,ruby,python這些語言不同,在c#和java的世界里面,原子對(duì)象就是類(當(dāng)然還有struct和基本變量),而不是其他語言的函數(shù),我們可以實(shí)例化一個(gè)類,實(shí)例化一個(gè)變量,但不可以直接new 一個(gè)函數(shù).也就是表面上看,我們是沒辦法像js那樣將函數(shù)進(jìn)行實(shí)例化和傳遞的.這也是為什么直到Java 7閉包才被姍姍來遲的加入java特性中。但對(duì)C#來說這些只是表象,我剛學(xué)c#的時(shí)候,看到最多的解釋委托的話就是:委托啊,就相當(dāng)于c++里面的函數(shù)指針啦.這句話雖然籠統(tǒng),但卻是有一定道理,通過委托特別是匿名委托這層對(duì)象的包裝,我們就可以突破無法將函數(shù)當(dāng)做對(duì)象傳遞的限制了.

  好像這里還是沒講到閉包和委托的關(guān)系,好吧,我太啰嗦了,下面從概念開始講.

  閉包其實(shí)就是使用的變量已經(jīng)脫離其作用域,卻由于和作用域存在上下文關(guān)系,從而可以在當(dāng)前環(huán)境中繼續(xù)使用其上文環(huán)境中所定義的一種函數(shù)對(duì)象.

  好拗口,程序員,還是用示例來說明更好理解.

  首先來個(gè)最簡單的javascript中常常見到的關(guān)于閉包的例子:

  1.   function f1(){  
  2.   var n=999;  
  3.   return function(){  
  4.   alert(n); // 999  
  5.   return n;  
  6.   }  
  7.   }  
  8.   var a =f1();  
  9.   alert(a()); 

  這段代碼翻譯成C#代碼就是這樣:

  1.   public class TCloser  
  2.   {  
  3.   public Func<int> T1()  
  4.   {  
  5.   var n = 999;  
  6.   return () =>  
  7.   {  
  8.   Console.WriteLine(n);  
  9.   return n;  
  10.   };  
  11.   }  
  12.   }  
  13.   class Program{  
  14.   static void Main(){  
  15.   var a =new TCloser();  
  16.   var b = a.T1();  
  17.   Console.WriteLine(b());  
  18.   }  
  19.   } 

  從上面的代碼我們不難看到,變量n實(shí)際上是屬于函數(shù)T1的局部變量,它本來生命周期應(yīng)該是伴隨著函數(shù)T1的調(diào)用結(jié)束而被釋放掉的,但這里我們卻在返回的委托b中仍然能調(diào)用它,這里正是閉包所展示出來的威力,因?yàn)門1調(diào)用返回的匿名委托的代碼片段中我們用到了n,而在編譯器看來,這些都是合法的,因?yàn)榉祷氐奈衎和函數(shù)T1存在上下文關(guān)系,也就是說匿名委托b是允許使用它所在的函數(shù)或者類里面的局部變量的,于是編譯器通過一系列動(dòng)作(具體動(dòng)作我們后面再說)使b中調(diào)用的函數(shù)T1的局部變量自動(dòng)閉合,從而使該局部變量滿足新的作用范圍。

  因此如果你看到.net中的閉包,你就可以像js中那樣理解它,由于返回的匿名函數(shù)對(duì)象是在函數(shù)T1中生成的,因此相當(dāng)于它是屬于T1的一個(gè)屬性。如果你把T1的對(duì)象級(jí)別往上提升一個(gè)層次就很好理解了,這里就相當(dāng)于T1是一個(gè)類,而返回的匿名對(duì)象則是T1的一個(gè)屬性,對(duì)屬性而言,它可以調(diào)用它所寄存的對(duì)象T1的任何其他屬性或者方法,包括T1寄存的對(duì)象TCloser內(nèi)部的其他屬性。如果這個(gè)匿名函數(shù)會(huì)被返回給其他對(duì)象調(diào)用,那么編譯器會(huì)自動(dòng)將匿名函數(shù)所用到的方法T1中的局部變量的生命周轉(zhuǎn)期自動(dòng)提升并與匿名函數(shù)的生命周期相同,這樣就稱之為閉合。

  也許你會(huì)說,這個(gè)返回的委托包含的變量n只是編譯器通過某種方式隱藏的對(duì)這個(gè)委托對(duì)象的一個(gè)同樣對(duì)象的賦值吧,那么我們?cè)賹?duì)比下面兩個(gè)方法:

  1.   public class TCloser{  
  2.   public Func<int> T1()  
  3.   {  
  4.   var n = 999;  
  5.   Func<int> result = () =>  
  6.   {  
  7.   return n;  
  8.   };  
  9.   n = 10;  
  10.   return result;  
  11.   }  
  12.   public dynamic T2()  
  13.   {  
  14.   var n = 999;  
  15.   dynamic result =new { A = n };  
  16.   n = 10;  
  17.   return result;  
  18.   }  
  19.   static void Main(){  
  20.   var a = new TCloser();  
  21.   var b = a.T1();  
  22.   var c = a.T2();  
  23.   Console.WriteLine(b());  
  24.   Console.WriteLine(c.A);  
  25.   }  
  26.   } 

  ***輸出結(jié)果是什么呢?答案是10和999,因?yàn)殚]包的特性,這里匿名函數(shù)中所使用的變量就是實(shí)際T1中的變量,與之相反的是,匿名對(duì)象result里面的A只是初始化時(shí)被賦予了變量n的值,它并不是n,所以后面n改變之后A并未隨之而改變。這正是閉包的魔力所在。

  你可能會(huì)好奇.net本身并不支持函數(shù)對(duì)象,那么這樣的特性又是從何而來呢?答案是編譯器,我們一看IL代碼便會(huì)明白了。

  首先我給出c#代碼:

  1.   public class TCloser {  
  2.   public Func<int> T1(){  
  3.   var n = 10;  
  4.   return () =>  
  5.   {  
  6.   return n;  
  7.   };  
  8.   }  
  9.   public Func<int> T4(){  
  10.   return () =>  
  11.   {  
  12.   var n = 10;  
  13.   return n;  
  14.   };  
  15.   }  
  16.   } 

  這兩個(gè)返回的匿名函數(shù)的唯一區(qū)別就是返回的委托中變量n的作用域不一樣而已,T1中變量n是屬于T1的,而在T4中,n則是屬于匿名函數(shù)本身的。但我們看看IL代碼就會(huì)發(fā)現(xiàn)這里面的大不同了:

  1.   .method public hidebysig instance class [mscorlib]System.Func`1<int32> T1() cil managed{  
  2.   .maxstack 3  
  3.   .locals init (  
  4.   [0] class ConsoleApplication1.TCloser/<>c__DisplayClass1 CS$<>8__locals2,  
  5.   [1] class [mscorlib]System.Func`1<int32> CS$1$0000)  
  6.   L_0000: newobj instance void ConsoleApplication1.TCloser/<>c__DisplayClass1::.ctor()  
  7.   L_0005: stloc.0  
  8.   L_0006: nop  
  9.   L_0007: ldloc.0  
  10.   L_0008: ldc.i4.s 10  
  11.   L_000a: stfld int32 ConsoleApplication1.TCloser/<>c__DisplayClass1::n  
  12.  L_000f: ldloc.0  
  13.   L_0010: ldftn instance int32 ConsoleApplication1.TCloser/<>c__DisplayClass1::<T1>b__0()  
  14.   L_0016: newobj instance void [mscorlib]System.Func`1<int32>::.ctor(object, native int)  
  15.   L_001b: stloc.1  
  16.   L_001c: br.s L_001e  
  17.   L_001e: ldloc.1  
  18.   L_001f: ret  
  19.   }  
  20.   .method public hidebysig instance class [mscorlib]System.Func`1<int32> T4() cil managed  
  21.   {  
  22.   .maxstack 3  
  23.   .locals init (  
  24.   [0] class [mscorlib]System.Func`1<int32> CS$1$0000)  
  25.   L_0000: nop  
  26.   L_0001: ldsfld class [mscorlib]System.Func`1<int32> ConsoleApplication1.TCloser::CS$<>9__CachedAnonymousMethodDelegate4  
  27.   L_0006: brtrue.s L_001b  
  28.   L_0008: ldnull  
  29.   L_0009: ldftn int32 ConsoleApplication1.TCloser::<T4>b__3()  
  30.   L_000f: newobj instance void [mscorlib]System.Func`1<int32>::.ctor(object, native int)  
  31.   L_0014: stsfld class [mscorlib]System.Func`1<int32> ConsoleApplication1.TCloser::CS$<>9__CachedAnonymousMethodDelegate4  
  32.   L_0019: br.s L_001b  
  33.   L_001b: ldsfld class [mscorlib]System.Func`1<int32> ConsoleApplication1.TCloser::CS$<>9__CachedAnonymousMethodDelegate4  
  34.   L_0020: stloc.0  
  35.   L_0021: br.s L_0023  
  36.   L_0023: ldloc.0  
  37.   L_0024: ret  
  38.   } 

  看IL代碼你就會(huì)很容易發(fā)現(xiàn)其中究竟了,在T1中,函數(shù)對(duì)返回的匿名委托構(gòu)造的是一個(gè)類,名稱為newobj instance void ConsoleApplication1.TCloser/<>c__DisplayClass1::.ctor(),而在T4中,則是仍然是一個(gè)普通的Func委托,只不過級(jí)別變?yōu)轭惣?jí)別了而已。

  那我們接著看看T1中聲明的類c__DisplayClass1是何方神圣:

  1.   .class auto ansi sealed nested private beforefieldinit <>c__DisplayClass1  
  2.   extends [mscorlib]System.Object{  
  3.   .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor()  
  4.   .method public hidebysig specialname rtspecialname instance void .ctor() cil managed{}  
  5.   .method public hidebysig instance int32 <T1>b__0() cil managed{}  
  6.   .field public int32 n  
  7.   } 

  看到這里想必你已經(jīng)明白了,在C#中,原來閉包只是編譯器玩的花招而已,它仍然沒有脫離.NET對(duì)象生命周期的規(guī)則,它將需要修改作用域的變量直接封裝到返回的類中變成類的一個(gè)屬性n,從而保證了變量的生命周期不會(huì)隨函數(shù)T1調(diào)用結(jié)束而結(jié)束,因?yàn)樽兞縩在這里已經(jīng)成了返回的類的一個(gè)屬性了。

  看到這里我想大家應(yīng)該大體上了解閉包的來龍去脈了吧,閉包其實(shí)和類中其他屬性、方法是一樣的,它們的原則都是下一層可以暢快的調(diào)用上一層定義的各種設(shè)定,但上一層則不具備訪問下一層設(shè)定的能力。即類中方法里的變量可以自由訪問類中的所有屬性和方法,而閉包又可以訪問它的上一層即方法中的各種設(shè)定。但類不可以訪問方法的局部變量,同理,方法也不可以訪問其內(nèi)部定義的匿名函數(shù)所定義的局部變量。

  這正是C#中的閉包,它通過超越j(luò)ava語言的委托打下了閉包的***步基礎(chǔ),隨后又通過各種語法糖和編譯器來實(shí)現(xiàn)如今在.NET世界全面開花的Lamda和LINQ.也使得我們能夠編寫出更加簡潔優(yōu)雅的代碼。

  附:后面是吐槽,與上文無關(guān),大家可以略過,這篇文章其實(shí)兩年之前在給同事講C#閉包的時(shí)候就有想法整理出來和大家分享了,不過因?yàn)樯睿ぷ鳎蛟S主要還是自己太懶的原因而拖著沒動(dòng)筆,到今天早上看到園友抱怨國內(nèi)教書育人的氛圍才最終決定利用晚上時(shí)間把它整理,然后放出來。我個(gè)人認(rèn)為國內(nèi)技術(shù)圈子的氛圍尚可,雖然仍然很多浮躁和易怒在圈子里徘徊。但我們想想國內(nèi)IT人的生存空間就容易理解了。每天最理想的情況朝9晚6的干活,晚上加班,周末加班這些都是常事,而對(duì)我們而言,只要想寫出一些經(jīng)過細(xì)細(xì)思考的東西都至少需要2個(gè)小時(shí)以上,而且***中間不要有人來打擾,這也就注定我們?cè)诎滋旃ぷ鲿r(shí)候很難完全有時(shí)間靜下來組織語言,刨掉這些時(shí)間,留給我們自己的生活時(shí)間又有多少呢?所以我每次看到有園友發(fā)表帖子的時(shí)間是晚上1點(diǎn),2點(diǎn)甚至更晚,都毫不意外,

  我們并非專業(yè)寫手,也不像國外IT人那樣有充足的閑暇時(shí)光可以鉆研自己的***,我們賺著他們的零頭,買著比他們本子價(jià)格更貴的筆記本,擔(dān)著比他們更高房價(jià)的壓力來生活,這樣的生活條件下我們這些可愛的社區(qū)(不僅限于cnblogs,javaeye,phpchina等)Geek們?nèi)匀蝗绱嘶钴S和熱情,你還能抱怨什么呢?你要知道你看到的每篇文章(如果是工作人士的話)都是他們晚上從9點(diǎn)寫到12點(diǎn)的生活點(diǎn)滴啊。

  所以,以后不要抱怨國內(nèi)IT氛圍吧,相對(duì)這個(gè)社會(huì)其他各行各業(yè)的浮躁,我覺得我們的IT圈子已經(jīng)是很樂于分享的一個(gè)群體了。而且除了因?yàn)?ldquo;天下武功,源自歐美,滯后于英語國家”的緣故,我們有些技術(shù)確實(shí)要晚些才能跟上國外社區(qū)的腳步,但對(duì)于一些基礎(chǔ)知識(shí)的解釋,已經(jīng)有很多中文的文章解釋得很不錯(cuò)了。像我以前在理解閉包的時(shí)候, javaeye上看到的一大堆,像WIKI,像阮一峰的文章,我個(gè)人認(rèn)為對(duì)中文用戶是足夠了。當(dāng)然,這只是我個(gè)人的觀點(diǎn),大家不必較勁。

原文鏈接:http://www.cnblogs.com/frankfang/archive/2011/08/03/2125663.html

責(zé)任編輯:彭凡 來源: 博客園
相關(guān)推薦

2016-09-18 20:53:16

JavaScript閉包前端

2023-09-11 08:20:17

對(duì)象閉包底層

2011-05-23 13:54:04

閉包

2009-08-20 18:30:33

C# ReaderWr

2011-09-21 10:56:31

C#結(jié)構(gòu)

2009-09-17 17:13:54

C#數(shù)組

2009-08-14 10:51:43

2022-10-24 08:08:27

閉包編譯器

2009-08-07 11:26:53

C#數(shù)組結(jié)構(gòu)

2009-08-12 11:24:25

C# String對(duì)象

2009-08-31 09:37:09

C# Employee

2009-08-19 17:12:18

C# Connecti

2009-08-26 13:15:38

C#選擇控制

2009-08-26 15:46:01

C#匿名類型

2009-08-25 16:16:43

C# oledbcon

2009-08-14 17:58:05

C#接口方法

2009-08-20 10:24:52

C#開發(fā)WinForm

2009-09-02 15:41:21

C# HTTPWebR

2009-08-06 15:30:23

C#類型系統(tǒng)

2009-08-17 17:13:50

C#安裝包制作
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 国产亚洲一区二区三区在线观看 | 日韩欧美天堂 | 大香在线伊779 | 精品福利在线视频 | 国产欧美日韩精品在线观看 | 91在线观看免费 | 国产欧美日韩在线一区 | 国产在线精品一区二区三区 | 日韩精品一区二区三区视频播放 | 日韩三级免费网站 | 国产在线观看一区二区三区 | 国产精品99久久久久久www | 国产人久久人人人人爽 | 99国产精品视频免费观看一公开 | 国产精品久久久99 | 精品视频一区二区 | 狠狠干网 | 日韩亚洲视频在线 | 免费日本视频 | 超碰97在线免费 | 久久精品成人热国产成 | 欧美日韩国产三级 | 欧美一区二区三区在线 | www成年人视频 | 午夜三级在线观看 | 国产精品18久久久久久白浆动漫 | 亚洲成人久久久 | 91久久网站 | 欧美在线精品一区 | 亚洲精品久久久久久久久久久 | 久久久av一区 | 午夜视频在线播放 | 国产高潮好爽受不了了夜色 | 国产视频亚洲视频 | 亚洲国产精品成人综合久久久 | 1204国产成人精品视频 | 日日碰狠狠躁久久躁婷婷 | 精品免费国产一区二区三区四区介绍 | 欧美日韩中文国产一区发布 | 欧美激情精品久久久久 | 亚洲精品国产a久久久久久 午夜影院网站 |