下面的這段C#代碼看似再普通不過
Stack stack = StackFactoryNew();
stackPush();
stackPush();
stackPush();
ConsoleWriteLine(stackPop());
ConsoleWriteLine(stackPop());
ConsoleWriteLine(stackPop());
運行結果
>>
>>
>>
但如果我告訴你Stack並不是一個普通的class Stack而是一個類型別名using Stack = SystemFunc<T R>它其實是一個委托你會不會覺得很神奇?說得更清楚一些StackFatoryNew()所創建的不是一個普通對象而是創建了一個閉包(Closure)
閉包是什麼?
那麼閉包究竟是什麼呢?目前有兩種流行的說法一種說法是閉包是在其詞法上下文中引用了自由變量的函數另一種說法認為閉包是由函數和與其相關的引用環境組合而成的實體兩種說法都對但我更傾向於第二種表述因為它明確地將閉包定義為實體從例子中我們可以看出閉包可以表現出對象的特征而普通的lambda函數或delegate更像是某個方法結合兩種定義我認為可以把閉包理解為帶狀態的函數
自由變量
我們先來看一個閉包的簡單例子
static Func<int int> AddX(int x) {
return (y) => x + y;
}
這裡的lambda函數(y) => x + y就是一個閉包它引用了其詞法上下文之外的自由變量x對AddX()求值將用代換x即(y)=> + y若再繼續求值AddX()()將得到 + =
狀態
下面我們將看到如何使閉包具有狀態
static Func<int> NewCounter() {
int x = ;
return () => ++x;
}
Func<int> counter = NewCounter();
ConsoleWriteLine(counter());
ConsoleWriteLine(counter());
ConsoleWriteLine(counter());
Func<int> counter = NewCounter();
ConsoleWriteLine(counter());
ConsoleWriteLine(counter());
ConsoleWriteLine(counter());
運行結果
>>
>>
>>
>>
>>
>>
我們通過NewCounter創建了一個閉包每次對閉包進行求值都會使其環境的局部變量x增這樣就體現了閉包的狀態同時我們注意到局部變量x對於不同的閉包是獨立的counter和counter並不共享同一個x
閉包 vs class
這裡我們可以和OOP程序做一個對比如果要用類來實現Counter我們會怎麼做呢?
class Counter{ //對應NewCounter
private int x; //對應局部變量int x
public Counter() { x = ; } //new Counter()對應NewCounter()
public int Count() { return ++x;} //對應() => ++x
}
和 上面的閉包程序相比從結構上看是不是有些類似呢?Counter類與NewCounter函數對應new Counter()與NewCounter()對應Counter類的私有成員x和NewCounter的局部變量x對應Counter類的 Count()方法與閉包對應
行為
除了狀態我們還需要讓閉包具備類似stackPush()和stackPop()這樣的對象行為由於閉包只擁有一個()運算符需要怎樣做才能使其具有多個方法呢?答案是高階函數(HigherOrder Function)看剛才Stack的例子
public enum Method {
Push Pop Top
}
public static Func<Method object> New() {
LinkedList<int> aList = new LinkedList<int>();
Func<Method object> func = (method) => {
switch (method) {
case MethodPush:
Action<int> push = (int aValue) => { aListAddLast(aValue); };
return push;
case MethodPop:
Func<int> pop = () => {
int value = aListLastValue;
aListRemoveLast();
return value;
};
return pop;
case MethodTop:
Func<int> top = () => { return aListLastValue; };
return top;
default:
return null;
}
};
return func;
}
NewStack()返回的是一個Func<Method object>類型的閉包它的參數是enum Method類型的返回值是objectNewStack()(MethodPush)將得到
Action<int> push = (int aValue) => { aListAddLast(aValue); };
這就是實際的Push方法!不過在調用之前還需要顯式轉換一下類型
(NewStack()(MethodPush) as Action<int>)();
最後我們利用C#的擴展方法(Extension Method)包裝一下讓這個調用更加簡潔
public static void Push(this Func<Method object> aStack int aValue){
(aStack(MethodPush) as Action<int>)(aValue);
}
public static int Pop(this Func<Method object> aStack){
return (int)(aStack(MethodPop) as Func<int>)();
}
public static int Top(this Func<Method object> aStack){
return (int)(aStack(MethodTop) as Func<int>)();
}
這樣我們就能寫出stackPush() stackPop()這樣很OO的代碼來了!通過這樣一步步地探索不知道您是否對函數式編程的閉包以及它和OOP對象的關系有了更深的理解呢?
模式
我們可以把上面在C#中用閉包創建對象的方法歸納為一種固定的模式不妨稱其為閉包工廠模式(Closure Factory Pattern)模式要點包括
定義一個工廠類XXFactory提供創建閉包對象的靜態工廠方法New
在New方法的內定義局部對象作為閉包對象的狀態m_States
定義enum Method表示對象所具有的方法
在New方法內創建並返回一個引用m_States的閉包closureObject其類型為Func<Method object>
closureObject接受Method參數返回表示該方法的閉包(或普通委托)的methodObject
通過擴展方法為Func<Method object>定義擴展方法為closureObject的方法調用加上語法糖衣
From:http://tw.wingwit.com/Article/program/ASP/201311/21828.html