我們的進步 源於您的支持
C#函數式程序設計之用閉包封裝數據發布時間:2014-04-15    來源:    點擊次數:

如果一個程序設計語言能夠用高階函數解決問題,則意味著數據作用域問題已十分突出。當函數可以當成參數和返回值在函數之間進行傳遞時,編譯器利用閉包擴展變量的作用域,以保證隨時能得到所需要的數據。

 
C#函數式程序設計之作用域
 
在C#中,變量的作用域是嚴格確定的。其本質是所有代碼生存在類的方法中、所有變量隻生存於聲明它們的模塊中或者之後的代碼中。變量的值是可變的,一個變量越是公開,帶來的問題就越嚴重。一般的原則是,變量的值最好保持不變,或者在最小的作用域內保存其值。一個純函數最好隻使用在自己的模塊中定義的變量值,不訪問其作用域之外的任何變量。
 
遺憾的是,有時我們無法把變量的值限製於函數的範圍內。如果在程序的初始化時定義了幾個變量,在後麵需要反複用到它們,怎麽辦?一個可能的辦法是使用閉包。
 
C#函數式程序設計之閉包機製
 
為了理解閉包的本質,我們分析幾個使用閉包的例子:
 
namespace Closures
{
    class Closures
    {
        static void Closures()
        {
            Console.WriteLine(GetClosureFunc()(30));
        }
 
        static Func<int,int> GetClosureFunc()
        {
            int val = 10;
            Func<int, int> internalAdd = x => x + val;
            Console.WriteLine(internalAdd(10));
            val = 30;
            Console.WriteLine(internalAdd(10));
            return internalAdd;
        }
    }
}
此代碼的結果輸出是多少?答案是20  40  60,前麵兩個值,大家應該很容易就能看出來,但第三個值為什麽是60呢?先來看看程序的執行流程:Closures函數調用GetClosureFunc函數並進入其中。函數調用語句中帶了一個參數30。這是由於GetClosureFunc返回的是一個函數,即執行時再次調用了這個函數,進入GetClosureFunc函數中,首先val的值為10,通過internalAdd方法傳入一個值10,因此第一個輸出值為20,往下走,val的值變成30,通過internalAdd方法傳入值10,於是第二個輸出值為40。從這裏我們大致可以看出,局部函數和局部變量如何在同一個作用域中起作用,顯然,對局部變量的改變會影響internalAdd的值,盡管變量的改變發生在internalAdd最初的創建之後。最後,GetClosureFunc返回了internalAdd方法,以參數30再次調用這個函數,於是,結果成為60。
 
初看起來,這並不真正符合邏輯。val應該是一個局部變量,它生存在棧中,當GetClosureFunc函數返回時,它就不在了,不是麽?確實如此,這正是閉包的目的,當編譯器會明白無誤地警告這種情況會引起程序的崩潰時阻止變量值超出其作用域之外。
 
從技術角度來看,數據保存的位置很重要,編譯器創建一個匿名類,並在GetClosureFunc中創建這個類的實例——如果不需要閉包起作用,則那個匿名函數隻會與GetClosureFunc生存在同一個類中,最後,局部變量val實際上不再是一個局部變量,而是匿名類中的一個字段。其結果是,internalAdd現在可以引用保存在匿名類實例中的函數。這個實例中也包含變量val的數據。隻要保持internalAdd的引用,變量val的值就一直保存著。
 
下麵這段代碼說明編譯器在這種情形下采用的模式:
 
 
private sealed class DisplayClass
{
    public int val;
 
    public int AnonymousFunc(int x)
    {
        return x + this.val;
    }
 
    private static Func<int, int> GetClosureFunc()
    {
        DisplayClass displayClass = new DisplayClass();
        displayClass.val = 10;
        Func<int, int> internalAdd = displayClass.AnonymousFunc;
        Console.WriteLine(internalAdd(10));
        displayClass.val = 30;
        Console.WriteLine(internalAdd(10));
        return internalAdd;
    }
}
回到動態創建函數思想:現在可以憑空創建新的函數,而且它的功能因參數而異。例如,下麵這個函數把一個靜態值加到一個參數上:
 
 
private static void DynamicAdd()
{
    var add5 = GetAddX(5);
    var add10 = GetAddX(10);
    Console.WriteLine(add5(10));
    Console.WriteLine(add10(10));
}
 
private static Func<int,int> GetAddX(int staticVal)
{
    return x => staticVal + x;
}
這個原理正是許多函數構建技術的基礎,這種方法顯然與方法重載等麵向對象方法相對應。但是與方法重載不同,匿名函數的創建可以在運行時動態發生,隻需受另一個函數中的一行代碼觸發。為使某個算法更加容易讀和寫而使用的特殊函數可以在調用它的方法中創建,而不是再類級別上胡亂添加函數或方法——這正是函數模塊化的核心思想。

信息分類
Copyright © 2012-2023 hnlczx.com Rights Reserved 蘇州成年在线观看信息技術有限公司    蘇ICP備13042621號
收縮
  • 電話谘詢

  • 13862425143
網站地圖 亚洲小妇人&&国产b站mv免费的小妇人&&b站大片视频&&b站直播高清视频在线观看&&b站mv永久免费视频&&成为视频人的App 又粗又爽又猛的视频>>又湿又大又免费的视频>>又爽又大又刺激视频免费看>>免费国产美女视频永久免费>>又大又硬又爽>>又爽又色 免费又粗又猛又爽的视频&&aaaaaa免费国产大片&&国产又大又粗又猛亚洲&&又粗又猛又爽视频免费&&免费观看床上刺激片真人直播&&免费看男女视频又爽又猛 又大又粗又刺激免费视频&&又刺激又爽又好看免费视频&&男女在做羞羞的免费视频&&一边亲一边摸一边揉免费视频&&刺激逼真的特效视频&&snh48大片mv免费 欧洲mv日韩mv国产mv&&日日夜夜免费视频&&又湿又紧又爽的视频免费观看&&看了就湿视频&&污拔插拔视频入口免费&&又湿又爽男女免费视频 又粗又猛又爽免费视频_b站免费进入永久全球_b站国产免费进入视频_b站国产大片在线进入_国产b站的真人动作片_b站视频国产精选 每日更新国产b站视频&&免费b站看大片真人直播&&在线观看国产b站视频女子监狱&&成年人精品日韩&&成年人片免费一二区视频&&成年人永久免费视频 手机在线观看精品专区&&手机视频 在线 久久&&手机在线综合一区二区&&国产免费手机在线视频&&手机在线一区二区&&手机在线精品免费观看 最近最新手机免费视频_手机在线视频3344vvg密桃app_手机在线视频3344vvg线直播_手机在线永久在线视频_手机影视在线专区_手机在线福小视频 国产专区手机在线&&手机在线直播视频3344&&手机视频在线播放&&99久&&精品免费人成视频三区&&9999视频精品全部免费6 精品一二三区免费看_免费手机视频电影去哪里找_久久手机免费视频_手机久久男人资源站_精品人伦一区二区三区蜜桃黑人_91夜夜夜精品一区二区 精品国产一二三区视频>>欧洲精品码一区二区三区免费看>>久久一区二区福利视频>>成年永久一区二区三区免费视频>>亚洲午夜精品一区二区三区>>日韩一区二区 综合视频在线 不卡_国产中文 高清_中文字幕 国产不卡_天天看国产大片高清视频_国产 欧美 高清 中字_国产不卡dvd免费 国精产品与非国精产品的区别_91NBA免费网站入口_三叶草亚洲码和国际码的区别_九一免费国产网站NBA_91TVnba免费网站入口_m.17天堂com入口