小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

閉包和對象的關(guān)系

 quasiceo 2016-07-17

閉包和對象的關(guān)系

下面的這段C#3.0代碼看似再普通不過:

Stack stack = StackFactory.New();
stack.Push(1);
stack.Push(2);
stack.Push(3);
Console.WriteLine(stack.Pop());
Console.WriteLine(stack.Pop());
Console.WriteLine(stack.Pop());

運(yùn)行結(jié)果:

>>3

>>2

>>1

但如果我告訴你Stack并不是一個普通的class Stack,而是一個類型別名:using Stack = System.Func<T1, R1>,它其實是一個委托,你會不會覺得很神奇?說得更清楚一些,StackFatory.New()所創(chuàng)建的不是一個普通對象,而是創(chuàng)建了一個閉包(Closure)。

 

閉包是什么?

那么閉包究竟是什么呢?目前有兩種流行的說法:一種說法是閉包是在其詞法上下文中引用了自由變量的函數(shù);另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實體。兩種說法都對,但我更傾向于第二種表述,因為它明確地將閉包定義為“實體”。從例子中我們可以看出,閉包可以表現(xiàn)出對象的特征,而普通的lambda函數(shù)或delegate更像是某個方法。結(jié)合兩種定義,我認(rèn)為可以把閉包理解為帶狀態(tài)的函數(shù)。

 

自由變量

我們先來看一個閉包的簡單例子:

static Func<int, int> AddX(int x) {
    return (y) => x + y;
}

這里的lambda函數(shù)(y) => x + y就是一個閉包,它引用了其詞法上下文之外的自由變量x。對AddX(8)求值將用8代換x,即(y)=>8 + y;若再繼續(xù)求值A(chǔ)ddX(8)(100)將得到8 + 100 = 108。

 

狀態(tài)

下面我們將看到如何使閉包具有狀態(tài):

static Func<int> NewCounter() {
    int x = 0;
    return () => ++x;
}

Func<int> counter1 = NewCounter();
Console.WriteLine(counter1());
Console.WriteLine(counter1());
Console.WriteLine(counter1());

Func<int> counter2 = NewCounter();
Console.WriteLine(counter2());
Console.WriteLine(counter2());
Console.WriteLine(counter2());

運(yùn)行結(jié)果:

>>1

>>2

>>3

>>1

>>2

>>3

我們通過NewCounter創(chuàng)建了一個閉包,每次對閉包進(jìn)行求值都會使其環(huán)境的局部變量x增1,這樣就體現(xiàn)了閉包的狀態(tài)。同時,我們注意到局部變量x對于不同的閉包是獨(dú)立的,counter1和counter2并不共享同一個x。

 

閉包 vs class

這里我們可以和OOP程序做一個對比,如果要用類來實現(xiàn)Counter我們會怎么做呢?

class Counter{ //對應(yīng)NewCounter

    private int x; //對應(yīng)局部變量int x

    public Counter() { x = 0; } //new Counter()對應(yīng)NewCounter()

    public int Count() { return ++x;} //對應(yīng)() => ++x

}

和 上面的閉包程序相比,從結(jié)構(gòu)上看是不是有些類似呢?Counter類與NewCounter函數(shù)對應(yīng);new Counter()與NewCounter()對應(yīng);Counter類的私有成員x和NewCounter的局部變量x對應(yīng);Counter類的 Count()方法與閉包對應(yīng)。

 

行為

除了狀態(tài),我們還需要讓閉包具備類似stack.Push()和stack.Pop()這樣的對象行為。由于閉包只擁有一個()運(yùn)算符,需要怎樣做才能使其具有多個方法呢?答案是高階函數(shù)(Higher-Order Function)。看剛才Stack的例子:

public enum Method {
    Push, Pop, Top
}

public static Func<Method, object> NewStack() {
    LinkedList<int> aList = new LinkedList<int>();
    Func<Method, object> func = (method) => {
        switch (method) {
            case Method.Push:
                Action<int> push = (int aValue) => { aList.AddLast(aValue); };
                return push;
            case Method.Pop:
                Func<int> pop = () => {
                    int value = aList.Last.Value;
                    aList.RemoveLast();
                    return value;
                };
                return pop;
            case Method.Top:
                Func<int> top = () => { return aList.Last.Value; };
                return top;
            default:
                return null;
        }
    };
    return func;
}

NewStack()返回的是一個Func<Method, object>類型的閉包,它的參數(shù)是enum Method類型的,返回值是object。NewStack()(Method.Push)將得到:

Action<int> push = (int aValue) => { aList.AddLast(aValue); };

 

這就是實際的Push方法!不過,在調(diào)用之前還需要顯式轉(zhuǎn)換一下類型:

(NewStack()(Method.Push) as Action<int>)(1);

 

最后,我們利用C#3.0的擴(kuò)展方法(Extension Method)包裝一下,讓這個調(diào)用更加簡潔:

public static void Push(this Func<Method, object> aStack, int aValue){
    (aStack(Method.Push) as Action<int>)(aValue);
}

public static int Pop(this Func<Method, object> aStack){
    return (int)(aStack(Method.Pop) as Func<int>)();
}

public static int Top(this Func<Method, object> aStack){
    return (int)(aStack(Method.Top) as Func<int>)();
}

 

這樣,我們就能寫出stack.Push(1), stack.Pop()這樣很OO的代碼來了!通過這樣一步步地探索,不知道您是否對函數(shù)式編程的閉包以及它和OOP對象的關(guān)系有了更深的理解呢?

 

模式

我們可以把上面在C#3.0中用閉包創(chuàng)建對象的方法歸納為一種固定的模式,不妨稱其為閉包工廠模式(Closure Factory Pattern)。模式要點(diǎn)包括:

1. 定義一個工廠類XXFactory,提供創(chuàng)建閉包對象的靜態(tài)工廠方法New;

2. 在New方法的內(nèi)定義局部對象作為閉包對象的狀態(tài)m_States;

3. 定義enum Method表示對象所具有的方法;

4. 在New方法內(nèi)創(chuàng)建并返回一個引用m_States的閉包c(diǎn)losureObject,其類型為Func<Method, object>;

5. closureObject接受Method參數(shù),返回表示該方法的閉包(或普通委托)的methodObject;

6. 通過擴(kuò)展方法為Func<Method, object>定義擴(kuò)展方法,為closureObject的方法調(diào)用加上語法糖衣。

 

參考

閉包的概念、形式與應(yīng)用

The Beauty of Closures

posted on 2010-11-01 08:56 Todd Wei 閱讀(3824) 評論(13) 編輯 收藏

評論

#1樓 2010-11-01 09:01 顧曉北  

閉包?
  回復(fù)引用

#2樓 2010-11-01 09:23 沒一句正經(jīng)的業(yè)余程序員  

“一種說法是閉包是在其詞法上下文中引用了自由變量的函數(shù)”,這是從語法角度講。
”另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實體”,這是從語義角度講,實際編譯器實現(xiàn)可能未必如此。
和lisp、javascript等動態(tài)語言的實現(xiàn)相比,c#的閉包示例顯得好繁瑣。

  回復(fù)引用

#3樓[樓主] 2010-11-01 09:31 Todd Wei  

@ fzwudc
>>lisp、javascript等動態(tài)語言的實現(xiàn)相比,c#的閉包示例顯得好繁瑣
是的,C#的lambda需要進(jìn)行類型檢查而不是符號演算。
  回復(fù)引用

#4樓 2010-11-01 12:48 諾貝爾  

對閉包這個名字感到無語的人飄過
  回復(fù)引用

#5樓 2010-11-02 03:59 Ivony...  

其實,沒看出實用價值。

不過利用閉包來模擬成員變量,倒并不是新鮮玩意兒,JavaScript就是利用閉包來實現(xiàn)私有成員的。

的確很好玩。
  回復(fù)引用

#6樓 2010-11-02 08:14 空逸云  

引用一種說法是閉包是在其詞法上下文中引用了自由變量的函數(shù);另一種說法認(rèn)為閉包是由函數(shù)和與其相關(guān)的引用環(huán)境組合而成的實體。兩種說法都對,但我更傾向于第二種表述

相比較于LZ.我更傾向于第一種.原因其實就是你在使用Lambda/LINQ表達(dá)式的時候.調(diào)用到"外部"的變量.編譯器的生成代碼其實是生成一個"閉包"的class.這是我所理解的閉包.而且閉包帶來的還有就是你的變量的"域"的延長.
http://www.cnblogs.com/kongyiyun/archive/2010/10/15/1851866.html
  回復(fù)引用

#7樓 2010-11-02 17:23 虛假真人  

雖然沒完全明白這樣做比起類有什么好處,不過非常精彩!
  回復(fù)引用

#8樓[樓主] 2010-11-02 18:05 Todd Wei  

@ 虛假真人
閉包是FP中實現(xiàn)OOP的重要手段,所以,個人認(rèn)為閉包在FP語言中的意義大于在OOP語言中。在C#中閉包可以簡化delegate的創(chuàng)建(否則就只能定義class,然后再創(chuàng)建delegate),更易于使用函數(shù)式風(fēng)格編程,這樣更易于把各個模塊粘合起來。本文主要是揭示閉包和對象之間的聯(lián)系。
  回復(fù)引用

#9樓 2010-11-02 18:23 Albert Yann  

╮(╯▽╰)╭閉包,這概念從函數(shù)式過來的吧?
  回復(fù)引用

#10樓 2010-11-03 13:11 Ivony...  

引用Todd Wei:
@虛假真人
閉包是FP中實現(xiàn)OOP的重要手段,所以,個人認(rèn)為閉包在FP語言中的意義大于在OOP語言中。在C#中閉包可以簡化delegate的創(chuàng)建(否則就只能定義class,然后再創(chuàng)建delegate),更易于使用函數(shù)式風(fēng)格編程,這樣更易于把各個模塊粘合起來。本文主要是揭示閉包和對象之間的聯(lián)系。



在純粹函數(shù)式語言中,由于所有變量都是“不可變”的,閉包即使能捕獲到外界變量,但也只是等于多了幾個參數(shù),換言之并不能修改外部變量的值,是不能實現(xiàn)OOP的。
  回復(fù)引用

#11樓[樓主] 2010-11-03 13:29 Todd Wei  

@ Ivony...
是的,純函數(shù)式中的符號都是引用透明的值語義,沒法實現(xiàn)引用語義。stack.Push(1)之后和原來的stack在值語義下是不相等的。所以,如果要在純函數(shù)式下實現(xiàn)stack,寫出來只能是類似這個樣子:
Pop(Push(Push(New(),1), 2))
  回復(fù)引用

#12樓 2011-03-06 21:49 egmkang  

我寫過lua的閉包,這個C#的閉包確是太繁瑣了
  回復(fù)引用

#13樓[樓主] 2011-06-11 13:24 Todd Wei  

@ egmkang
沒看出來Lua的閉包比C#簡單呢?

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多