日韩久久久精品,亚洲精品久久久久久久久久久,亚洲欧美一区二区三区国产精品 ,一区二区福利

高階函數(shù)、委托與匿名方法

系統(tǒng) 2466 0

高階函數(shù)、委托與匿名方法

作者 趙劼 發(fā)布于 2009年4月17日 下午6時35分

高階函數(shù)(higher-order function)是指把另一個函數(shù)作為參數(shù)或返回值的函數(shù)。例如在JavaScript語言中,F(xiàn)unction是頂級類型。一個函數(shù)就是類型為 Function的頂級對象,自然就可以作為另一個函數(shù)的參數(shù)或返回值。例如在Microsoft AJAX Library(ASP.NET AJAX的客戶端類庫)中有一個被廣泛使用的createDelegate方法。該方法接受一個對象A和一個函數(shù)F作為參數(shù),并返回一個函數(shù)R。當調(diào)用函 數(shù)R時,F(xiàn)函數(shù)將被調(diào)用,并且保證無論在什么上下文中,F(xiàn)的this引用都會指向?qū)ο驛:

    Function.createDelegate = 
    
      function
    
    (instance, func) {

 
    
      return function
    
    () {

 
    
      return 
    
    callback.apply(a, arguments);

 } 

}
  

委托是.NET平臺中一種特殊的類型。有人說,它是一種強類型的函數(shù)指針。這種說法雖然細節(jié)上略失偏頗,但是從功能和作用上講不無道理。有了委 托類型,一個方法就能被封裝成一個對象被作為另一個方法的參數(shù)或返回值,這自然就為.NET平臺上的語言(例如C#,VB.NET)引入了對高階函數(shù)的“ 原生支持” 1 。例如在System.Array類中就有許多靜態(tài)的高階函數(shù),其中的ConvertAll方法可謂是最常用的高階函數(shù)之一了:

    
      private static 
    
    
      DateTime 
    
    StringToDateTime(
    
      string 
    
    s) {

 
    
      return 
    
    
      DateTime
    
    .ParseExact(s, 
    
      "yyyy-MM-dd"
    
    , 
    
      null
    
    );

}




    
      static void 
    
    Main(
    
      string
    
    [] args) {

 
    
      string
    
    [] dateStrings = 
    
      new string
    
    [] {

 
    
      "2009-01-01"
    
    , 
    
      "2009-01-02"
    
    , 
    
      "2009-01-03"
    
    ,

 
    
      "2009-01-04"
    
    , 
    
      "2009-01-05"
    
    , 
    
      "2009-01-06"
    
    ,

 };



 
    
      DateTime
    
    [] dates = 
    
      Array
    
    .ConvertAll<
    
      string
    
    , 
    
      DateTime
    
    >(

 dateStrings, 
    
      new 
    
    
      Converter
    
    <
    
      string
    
    , 
    
      DateTime
    
    >(StringToDateTime));

}
  

ConvertAll 將一個數(shù)組映射為另一個數(shù)組,就好像Ruby中array類型的 map 方法一樣,但是如果您會發(fā)現(xiàn)ruby的“內(nèi)聯(lián)”寫法會方便許多。于是在C# 2.0中,又引入了匿名方法這一構(gòu)建委托對象的方式:

    
      string
    
    [] dateStrings = 
    
      new string
    
    [] {

 
    
      "2009-01-01"
    
    , 
    
      "2009-01-02"
    
    , 
    
      "2009-01-03"
    
    ,

 
    
      "2009-01-04"
    
    , 
    
      "2009-01-05"
    
    , 
    
      "2009-01-06"
    
    ,

};




    
      DateTime
    
    [] dates = 
    
      Array
    
    .ConvertAll<
    
      string
    
    , 
    
      DateTime
    
    >(

 dateStrings,

 
    
      delegate
    
    (
    
      string 
    
    s) {

 
    
      return 
    
    
      DateTime
    
    .ParseExact(s, 
    
      "yyyy-MM-dd"
    
    , 
    
      null
    
    );

 });


  

匿名方法并不只是“匿名”的方法,它甚至可以構(gòu)造一個閉包給開發(fā)帶來極大的便利。可見在2.0中已經(jīng)為高階函數(shù)在C#中的運用打下了堅實的基 礎。而且,由于新增了Lambda表達式和擴展方法等語言特性,再加上范型類型的自動判斷,在C# 3.0中使用匿名方法更是異常簡潔,甚至與ruby的語法如出一轍:

    
      IEnumerable
    
    <
    
      DateTime
    
    > dates = dateStrings.Select(

 s => 
    
      DateTime
    
    .ParseExact(s, 
    
      "yyyy-MM-dd"
    
    , 
    
      null
    
    ));


  

從理論上說,委托從在.NET 1.x環(huán)境中即得到了完整的支持,但是直到C# 3.0之后高階函數(shù)在.NET中的應用切實地推廣開來。善于使用高階函數(shù)的特性能夠有效地提高開發(fā)效率,同時使代碼變得優(yōu)雅、高效。為了方便開 發(fā),.NET 3.5中甚至定義了三種泛化的委托類型: Action<>、Predicate<>以及Func<> ,讓開發(fā)人員可 以在項目中直接使用。如今,微軟官方的各種框架和類庫(例如著名的 并行庫 )中對于高階函數(shù)的使用幾乎將其變成了一種事實標準。在這一點上,Lambda表達式和匿名方法可謂居功至偉。

高階函數(shù)的一個重要特點就是對參數(shù)方法的延遲執(zhí)行。例如,對于普通的方法調(diào)用方式來說:

    DoSomething(Method1(), Method2(), Method3());
  

代碼中涉及到的四個方法調(diào)用順序已經(jīng)完全確定:只有當Method1、Method2和Method3三個方法依次調(diào)用完畢并返回之后, DoSomething 方法才會執(zhí)行。然而,如果我們改變 DoSomething 方法的簽名,并使用這樣的方式:

    DoSomething(() => Method1(), () => Method2(), () => Method3());
  

這樣,四個方法中 DoSomething 方法肯定首先被調(diào)用,然后根據(jù)方法體內(nèi)的具體實現(xiàn),其余三個方法可能被調(diào)用任意次數(shù)——甚至一次也不會 被調(diào)用。利用這個特性,即“提供方法體,但是不執(zhí)行”,我們就可以在某些邏輯不確定的情況下避免不必要的開銷。例如,如果不使用高階函數(shù),一段處理數(shù)據(jù)的 邏輯可能是這樣的:

    
      void 
    
    Process(
    
      object 
    
    dataThatIsExpensiveToGet) {

 
    
      bool 
    
    canProcess = GetWhetherDataCanBeProcessedOrNot();

 
    
      if 
    
    (canProcess) {

 DoSomeThing(dataThatIsExpensiveToGet);

 }

}


  

在上例中, Process 方法只有在滿足特定前提的情況下才對參數(shù)進行處理,而且在很多時候這個前提條件必須在Process方法中才能判斷。 這時,如果參數(shù)本身需要昂貴的代價才能獲得,那么獲取參數(shù)的損耗就白白被浪費了。為了避免這種無謂的消耗,我們可以在設計Process方法API時使用 如下辦法:

    
      void 
    
    Process(
    
      Func
    
    <
    
      object
    
    > expensiveDataGetter) {

 
    
      bool 
    
    canProcess = GetWhetherDataCanBeProcessedOrNot();

 
    
      if 
    
    (canProcess) {

 
    
      object 
    
    dataToProcess = expensiveDataGetter();

 DoSomeThing(dataToProcess);

 }

}


  

這樣,我們就可以使用如下的方式來調(diào)用 Process 方法:

    
      // Process(GetExpensiveData(args));
      
Process(() => GetExpensiveData(args));

與注釋掉的代碼相比,消耗巨大 GetExpensiveData 方法并不會被直接調(diào)用,而只有在 Process 方法內(nèi)滿足前提條件時才會執(zhí)行。有時候,我們甚至可以在第一個參數(shù)方法滿足特定條件時才執(zhí)行另一個參數(shù)方法。在《 您善于使用匿名函數(shù)嗎? 》一文中的 CacheHelper 便是這樣一個例子:

    
      public static class 
    
    
      CacheHelper
      
{ public delegate bool CacheGetter <TData>( out TData data); public static TData Get<TData>( CacheGetter <TData> cacheGetter, /* get data from cache */
Func <TData> sourceGetter, /* get data from source (fairly expensive) */
Action <TData> cacheSetter /* set the data to cache */ ) { TData data; if (cacheGetter( out data)) { return data; } data = sourceGetter(); cacheSetter(data); return data; } }

CacheHelper Get 方法接受三個委托對象作為參數(shù),只有當?shù)谝粋€方法(從緩存中獲取對象)返回為False時,才會執(zhí)行第二個(從 相對昂貴的數(shù)據(jù)源獲取數(shù)據(jù))和第三個方法(將數(shù)據(jù)源中得到的數(shù)據(jù)放入緩存)。同時,這個示例也展示了高階方法的另一個常用特點:封裝一段通用的邏輯,將邏 輯中特定部分的交由外部實現(xiàn)——這不就是“模板方法(Template Method)模式”嗎?高階函數(shù)從某個角度可以看成是一種輕量級的模板方法實現(xiàn),它提供了模板方法中的主要特性,但是不需要使用“繼承”這種耦合性很高 的擴展方式。而且,由于可以為一個委托參數(shù)提供任意的實現(xiàn),我們也可以在某些場景下用它來代替“策略(Strategy)模式”的使用。

  不過也由此可見,高階函數(shù)并不一定需要“函數(shù)指針”或“委托類型”的支持。事實上,面向?qū)ο笳Z言中的對象可以攜帶方法,而一個方法可以接受另一 個對象作為參數(shù)(或返回一個對象),那么這個方法自然也就相當于一個接受或返回方法的“高階函數(shù)”了。例如,我們可以使用Java來實現(xiàn)如上的 CacheHelper 輔助類:

    
      public interface 
    
    Func<T> {

 T execute();

}




    
      public interface 
    
    Action<T> {

 
    
      void 
    
    execute(T data);

}




    
      public class 
    
    CacheHelper {

 
    
      public static
    
    <T> T get(

 Func<T> cacheGetter,

 Func<T> sourceGetter,

 Action<T> cacheSetter)

 {

 T data = cacheGetter.execute();

 
    
      if 
    
    (data == 
    
      null
    
    ) {

 data = sourceGetter.execute();

 cacheSetter.execute(data);

 }

 

 
    
      return 
    
    data;

 }

}
  

不過從C#的演變過程中可以看出,高階函數(shù)的特性要真正得到推廣,也必須由“匿名方法”等更多特性加以輔佐才行。Java中的“匿名類”與C#中的“匿名方法”有異曲同工之處,例如,開發(fā)人員同樣可以使用內(nèi)聯(lián)的寫法來調(diào)用 CacheHelper

    
      public 
    
    Object getData()

{

 
    
      return 
    
    CacheHelper.get(

 
    
      new 
    
    Func<Object>() {

 
    
      public 
    
    Object execute() {

 
    
      /* get data from cache */
      
return null ; } }, new Func<Object>() { public Object execute() { /* get data from source (fairly expensive) */
return null ; } }, new Action<Object>() { public void execute(Object data) { /* set the data to cache */
} }); }

可惜,有些時候類似的代碼在Java語言中相對并不那么實用。其原因可能是因為Java中“匿名類”語法較為復雜,且匿名類的內(nèi)部邏輯無法修改調(diào)用方法里的局部變量——由此也可對比出C#中匿名函數(shù)這一特性的美妙之處。

注1:嚴格來說,.NET只是提供了一個平臺,一個“運行時(CLR)”,但“高階函數(shù)”其實是個語言方面的概念。我們可以在.NET上實現(xiàn)任意一種語 言,而這種語言就算沒有得到平臺的直接支持,也能夠?qū)崿F(xiàn)“高階函數(shù)”這個特性。因此,之所以是“原生支持”,其實指的是.NET平臺對高階函數(shù)所需的特性 有著直接的支持,它使得C#或VB.NET等語言中能夠直接使用高階函數(shù)這一功能。

結(jié)論:

.NET 3.5對于創(chuàng)建委托對象的良好支持使得高階函數(shù)在.NET平臺上的使用得到了卓有成效的推廣。從微軟新發(fā)布的框架和類庫中來看,高階函數(shù)幾乎已經(jīng)成為了一 種事實標準。善于使用高階函數(shù)的特性能夠有效地提高開發(fā)效率,同時使代碼變得優(yōu)雅、高效。可以料想的到,善于使用高階函數(shù)會逐步成為一個優(yōu)秀的.NET開 發(fā)人員的必備技術(shù)。

高階函數(shù)、委托與匿名方法


更多文章、技術(shù)交流、商務合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 天台县| 观塘区| 广安市| 五常市| 神农架林区| 蓬安县| 镇远县| 香河县| 鲜城| 张家界市| 乐安县| 会东县| 宁阳县| 班戈县| 福安市| 蒲江县| 景德镇市| 汕头市| 久治县| 乌海市| 赤壁市| 周至县| 榆树市| 亳州市| 惠州市| 来凤县| 罗定市| 长垣县| 琼海市| 诸城市| 德清县| 溧水县| 手机| 昂仁县| 海宁市| 灌云县| 思茅市| 长葛市| 福海县| 东至县| 蒲城县|