日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢(xún)
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷(xiāo)解決方案
C#本地函數(shù)與Lambda表達(dá)式

本文轉(zhuǎn)載自微信公眾號(hào)「DotNET技術(shù)圈」,作者Vladimir Sadov。轉(zhuǎn)載本文請(qǐng)聯(lián)系DotNET技術(shù)圈公眾號(hào)。

創(chuàng)新互聯(lián)公司憑借在網(wǎng)站建設(shè)、網(wǎng)站推廣領(lǐng)域領(lǐng)先的技術(shù)能力和多年的行業(yè)經(jīng)驗(yàn),為客戶(hù)提供超值的營(yíng)銷(xiāo)型網(wǎng)站建設(shè)服務(wù),我們始終認(rèn)為:好的營(yíng)銷(xiāo)型網(wǎng)站就是好的業(yè)務(wù)員。我們已成功為企業(yè)單位、個(gè)人等客戶(hù)提供了成都網(wǎng)站制作、網(wǎng)站建設(shè)服務(wù),以良好的商業(yè)信譽(yù),完善的服務(wù)及深厚的技術(shù)力量處于同行領(lǐng)先地位。

C# 局部函數(shù)通常被視為 lambda 表達(dá)式的進(jìn)一步增強(qiáng)。雖然功能是相關(guān)的,但也存在重大差異。

Local Functions 是嵌套函數(shù)[1]功能的 C# 實(shí)現(xiàn)。一種語(yǔ)言在支持 lambdas 之后獲得對(duì)嵌套函數(shù)的支持幾個(gè)版本是有點(diǎn)不尋常的。通常情況相反。

Lambda 或一般的一流函數(shù)需要實(shí)現(xiàn)未在堆棧上分配且生命周期與需要它們的功能對(duì)象相關(guān)聯(lián)的局部變量。如果不依賴(lài)?yán)占蛲ㄟ^(guò)捕獲列表等解決方案將變量所有權(quán)的負(fù)擔(dān)減輕給用戶(hù),則幾乎不可能正確有效地實(shí)現(xiàn)它們。對(duì)于某些早期語(yǔ)言來(lái)說(shuō),這是一個(gè)嚴(yán)重的阻塞問(wèn)題。嵌套函數(shù)的簡(jiǎn)單實(shí)現(xiàn)不會(huì)遇到這種復(fù)雜情況,因此一種語(yǔ)言更常見(jiàn)的是僅支持嵌套函數(shù)而不支持 lambda。

無(wú)論如何,由于 C# 長(zhǎng)期以來(lái)一直使用 lambda,因此從差異和相似之處來(lái)看本地函數(shù)確實(shí)是有意義的。

Lambda 表達(dá)式

Lambda 表達(dá)式x => x + x是抽象地表示一段代碼以及它如何綁定到其詞法環(huán)境中的參數(shù)和變量的表達(dá)式。作為代碼的抽象表示,lambda 表達(dá)式不能單獨(dú)使用。為了使用由 lambda 表達(dá)式生成的值,需要將其轉(zhuǎn)換為更多內(nèi)容,例如委托或表達(dá)式樹(shù)。

 
 
 
  1. using System; 
  2. using System.Linq.Expressions; 
  3.  
  4. class Program 
  5.     static void Main(string[] args) 
  6.     { 
  7.         // can't do much with the lambda expression directly 
  8.         // (x => x + x).ToString();  // error 
  9.  
  10.         // can assign to a variable of delegate type and invoke 
  11.         Func f = (x => x + x); 
  12.         System.Console.WriteLine(f(21)); // prints "42" 
  13.  
  14.         // can assign to a variable of expression type and introspect 
  15.         Expression> e = (x => x + x); 
  16.         System.Console.WriteLine(e);     // prints "x => (x + x)" 
  17.     } 

有幾點(diǎn)值得注意:

  • lambdas 是產(chǎn)生函數(shù)值的表達(dá)式。
  • lambda 值的生命周期是無(wú)限的——從 lambda 表達(dá)式的執(zhí)行開(kāi)始,只要存在對(duì)該值的任何引用。這意味著 lambda 從封閉方法中使用或“捕獲”的任何局部變量都必須在堆上分配。由于 lambda 值的生命周期不受產(chǎn)生它的堆棧幀的生命周期的限制,因此不能在該堆棧幀上分配變量。
  • lambda 表達(dá)式要求在執(zhí)行 lambda 表達(dá)式時(shí)明確分配主體中使用的所有外部變量。lambda 的第一次和最后一次使用的時(shí)刻很少是確定性的,因此該語(yǔ)言假設(shè) lambda 值可以在創(chuàng)建后立即使用,只要它們是可訪問(wèn)的。因此,一個(gè) lambda 值在創(chuàng)建時(shí)必須是完全可用的,并且它使用的所有外部變量都必須明確分配。
 
 
 
  1. int x; 
  2.  
  3.      // ERROR: 'x' is not definitely assigned 
  4.      Func f = () => x; 
  • lambdas 沒(méi)有名字,也不能被象征性地引用。特別是 lambda 表達(dá)式不能遞歸聲明。

注意:可以通過(guò)調(diào)用分配給 lambda 的變量或傳遞給自應(yīng)用其參數(shù)的高階方法來(lái)創(chuàng)建遞歸 lambda(請(qǐng)參閱:C# 中的匿名遞歸[2]),但這不會(huì)表達(dá)真正的自我參照。

本地函數(shù)

局部函數(shù)基本上只是在另一個(gè)方法中聲明的方法,作為一種降低方法對(duì)其聲明范圍內(nèi)的可見(jiàn)性的方法。

自然地,局部函數(shù)中的代碼可以訪問(wèn)其包含范圍內(nèi)可訪問(wèn)的所有內(nèi)容——局部變量、封閉方法的參數(shù)、類(lèi)型參數(shù)、局部函數(shù)。一個(gè)值得注意的例外是外部方法標(biāo)簽的可見(jiàn)性。封閉方法的標(biāo)簽在局部函數(shù)中不可見(jiàn)。這只是普通的詞法范圍,它的工作原理與 lambdas 相同。

 
 
 
  1. public class C 
  2.     object o; 
  3.  
  4.     public void M1(int p) 
  5.     { 
  6.         int l = 123; 
  7.  
  8.         // lambda has access to o, p, l, 
  9.         Action a = ()=> o = (p + l); 
  10.     } 
  11.  
  12.     public void M2(int p) 
  13.     { 
  14.         int l = 123; 
  15.  
  16.         // Local Function has access to o, p, l, 
  17.         void a() 
  18.         { 
  19.           o = (p + l); 
  20.         } 
  21.     } 

與 lambda 的明顯區(qū)別在于局部函數(shù)具有名稱(chēng)并且可以在沒(méi)有任何間接方式的情況下使用。局部函數(shù)可以是遞歸的。

 
 
 
  1. static int Fac(int arg) 
  2.     int FacRecursive(int a) 
  3.     { 
  4.         return a <= 1 ? 
  5.                     1 : 
  6.                     a * FacRecursive(a - 1); 
  7.     } 
  8.  
  9.     return FacRecursive(arg); 

與 lambda 表達(dá)式的主要語(yǔ)義區(qū)別在于局部函數(shù)不是表達(dá)式,它們是聲明語(yǔ)句。在代碼執(zhí)行方面,聲明是非常被動(dòng)的實(shí)體。事實(shí)上,聲明并沒(méi)有真正被“執(zhí)行”。與標(biāo)簽等其他聲明類(lèi)似,局部函數(shù)聲明只是將函數(shù)引入包含范圍,而無(wú)需運(yùn)行任何代碼。

更重要的是,無(wú)論是聲明本身還是嵌套函數(shù)的常規(guī)調(diào)用都不會(huì)導(dǎo)致對(duì)環(huán)境的不確定捕獲。在簡(jiǎn)單和常見(jiàn)的情況下,如普通的調(diào)用/返回場(chǎng)景,捕獲的局部變量不需要進(jìn)行堆分配。

例子:

 
 
 
  1. public class C 
  2. {     
  3.     public void M() 
  4.     { 
  5.         int num = 123; 
  6.  
  7.         // has access to num 
  8.         void  Nested() 
  9.         { 
  10.            num++; 
  11.         } 
  12.  
  13.         Nested(); 
  14.  
  15.         System.Console.WriteLine(num); 
  16.     } 

上面的代碼大致相當(dāng)于(反編譯):

 
 
 
  1. public class C 
  2.   // A struct to hold "num" variable. 
  3.   // We are not storing it on the heap, 
  4.   // so it does not need to be a class 
  5.   private struct <>c__DisplayClass0_0 
  6.   { 
  7.       public int num; 
  8.   } 
  9.  
  10.   public void M() 
  11.   { 
  12.       // reserve storage for "num" in a display struct on the _stack_ 
  13.       C.<>c__DisplayClass0_0 env = default(C.<>c__DisplayClass0_0); 
  14.  
  15.       // num = 123 
  16.       env.num = 123; 
  17.  
  18.       // Nested() 
  19.       // note - passes env as an extra parameter 
  20.       C.g__a0_0(ref env); 
  21.  
  22.       // System.Console.WriteLine(num) 
  23.       Console.WriteLine(env.num); 
  24.   } 
  25.  
  26.     // implementation of the the "Nested()". 
  27.     // note - takes env as an extra parameter 
  28.     // env is passed by reference so it's instance is shared 
  29.     // with the caller "M()" 
  30.     internal static void g__a0_0(ref C.<>c__DisplayClass0_0 env) 
  31.     { 
  32.         env.num += 1; 
  33.     } 

請(qǐng)注意,上面的代碼直接調(diào)用了“Nested()”的實(shí)現(xiàn)(不是通過(guò)委托間接),并且沒(méi)有在堆上引入顯示存儲(chǔ)的分配(就像 lambda 會(huì)那樣)。局部變量存儲(chǔ)在結(jié)構(gòu)中而不是類(lèi)中。的生命周期num并沒(méi)有因?yàn)樗?中的使用而改變Nested(),所以它仍然可以在棧上分配。M()可以只通過(guò)num引用傳遞,但編譯器使用結(jié)構(gòu)體進(jìn)行打包,因此它可以傳遞所有本地變量,就像num只使用一個(gè) env 參數(shù)一樣。

另一個(gè)有趣的一點(diǎn)是,只要本地函數(shù)在給定范圍內(nèi)可見(jiàn),就可以使用它們。這是一個(gè)重要的事實(shí),使遞歸和相互遞歸的場(chǎng)景成為可能。這也使得本地函數(shù)聲明在源代碼中的確切位置在很大程度上變得不重要。

例如,封閉方法的所有變量必須在調(diào)用讀取它們的本地函數(shù)時(shí)明確分配,而不是在其聲明時(shí)。實(shí)際上,如果調(diào)用可以更早發(fā)生,那么在聲明時(shí)提出該要求將沒(méi)有任何好處。

 
 
 
  1. public void M() 
  2.     // error here - 
  3.     // Use of unassigned local variable 'num' 
  4.     Nested(); 
  5.  
  6.     int num; 
  7.  
  8.     // whether 'num' is assigned here or not is irrelevant 
  9.     void  Nested() 
  10.     { 
  11.        num++; 
  12.     } 
  13.  
  14.     num = 123; 
  15.  
  16.     // no error here - 'num' is assigned 
  17.     Nested(); 
  18.  
  19.     System.Console.WriteLine(num); 

此外 - 如果從未使用過(guò)局部函數(shù),它也不會(huì)比一段無(wú)法訪問(wèn)的代碼和任何變量更好,否則它會(huì)使用,不需要分配。

 
 
 
  1. public void M() 
  2. {         
  3.     int num; 
  4.  
  5.     // warning - Nested() is never used. 
  6.     void  Nested() 
  7.     { 
  8.        // no errors on unassigned 'num'. 
  9.        // this code never runs. 
  10.        num++; 
  11.     } 

那么,局部函數(shù)的目的是什么?

與 lambdas 相比,局部函數(shù)的主要價(jià)值主張是局部函數(shù)在概念上和運(yùn)行時(shí)開(kāi)銷(xiāo)方面都更簡(jiǎn)單。

Lambda 可以很好地充當(dāng)一類(lèi)函數(shù)[3]的角色,但有時(shí)您只需要一個(gè)簡(jiǎn)單的助手。分配給局部變量的 Lambda 可以完成這項(xiàng)工作,但存在間接開(kāi)銷(xiāo)、委托分配和可能的閉包開(kāi)銷(xiāo)。私有方法也有效,調(diào)用成本更低,但存在封裝問(wèn)題,或缺乏封裝。這樣的助手對(duì)包含類(lèi)型中的每個(gè)人都是可見(jiàn)的。太多這樣的幫手會(huì)導(dǎo)致嚴(yán)重的混亂。

局部函數(shù)非常適合這種情況。調(diào)用本地函數(shù)的開(kāi)銷(xiāo)與調(diào)用私有方法的開(kāi)銷(xiāo)相當(dāng),但使用其他不應(yīng)調(diào)用的方法污染包含類(lèi)型沒(méi)有問(wèn)題。

http://mustoverride.com/local_functions/

References

[1] 嵌套函數(shù): https://en.wikipedia.org/wiki/Nested_function

[2] C# 中的匿名遞歸: https://blogs.msdn.microsoft.com/wesdyer/2007/02/02/anonymous-recursion-in-c/ 

[3] 一類(lèi)函數(shù): https://en.wikipedia.org/wiki/First-class_function


文章標(biāo)題:C#本地函數(shù)與Lambda表達(dá)式
當(dāng)前網(wǎng)址:http://www.5511xx.com/article/cdopess.html