电脑故障

位置:IT落伍者 >> 电脑故障 >> 浏览文章

闭包工厂模式:从Lambda到Object


发布日期:2023/11/12
 

下面的这段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的方法调用加上语法糖衣

上一篇:避免覆盖通过继承得到的名字

下一篇:MSNPSharp发送消息过程详解