C#-continuation-passing style(CPS)

简介:

如果你还不是很了解CPS是什么,那么推荐几个链接给你(希望你的英语要给力啊):

http://blogs.msdn.com/b/wesdyer/archive/2007/12/22/continuation-passing-style.aspx

http://en.wikipedia.org/wiki/Continuation-passing_style

http://blogs.msdn.com/b/ericlippert/archive/2010/10/22/continuation-passing-style-revisited-part-two-handwaving-about-control-flow.aspx

CPS(continuation-passing style):字面可以理解为后继式传递格式,这是在函数式编程中的一个特性。但在C#中Lambda表达式和Action<T> 泛型委托结合起来,能够很好地实现这一特性。

原来我们用结构化处理异常的方式:

void Q()
{
try
{
B(A());
}
catch
{
C();
}
D();
}

int A(int a)
{
throw;
return 0; // 不可达,暂时忽略

}
void B(int x) { //to do something }
void C() { //to do something }
void D() { //to do something }

这些方法调用是否面向对象,这不是我们要讨论的话题。但是不管怎么说,try{...}catch{...}finally{...}这种结构化的异常处理方式,至今还是在被广泛使用。

结构化的一个特点,耦合性强,关联度高。就像流水线一样紧密结合。

让我们先看一下,这个调用流程:

首先执行方法A,如果A的调用未产生任何异常,则返回结果给B作为参数,调用B方法,如果方法B执行正常。则方法C不会被执行,直接跳到方法D开始执行。

如果方法A或B任何一方产生异常,执行将会被中止。调用将会跳转到方法C执行(当然前提是,该异常能被顺利捕获到)。最后再调用方法D。

其实CLR在采用结构化的异常处理机制时,实现了一些帅选器和处理器等内部和语言机制,可参考《CLR Via C# 3.0》。但是,既然我们说这种异常处理方式是一种环环相扣的,类似于流式的,为什么我们不能模拟采用CPS来实现呢?

对于ABCD四个方法,我们都考虑两种情况(其实就是一种if ...else....结构)一种情况:方法调用成功;一种情况方法调用失败。

于是,可以这样定义:

Action<T>:接受一个类型为T的参数,并且没有返回值;

void A(Action<int> normal, Action error);

void B(int x, Action normal, Action error) { whatever }
void C(Action normal, Action error) { whatever }
void D(Action normal, Action error) { whatever }

注:所有的normal,都可以想象为,我们通常不考虑异常的方法体,所有的error都可以认为对原方法体中出现异常的处理方法

这样对try块的处理逻辑抽象为:

Try (
/* tryBody */ (bodyNormal, bodyError)=>A(
/* normal for A */ x=>B(x, bodyNormal, bodyError),
/* error for A */ bodyError),
/* catchBody */ C,
/* outerNormal */ ()=>D(qNormal, qError),
/* outerError */ qError );
首先,从外部来看try块只能有两个出口:

由try——>outerNormal,将执行:()=>D(qNormal, qError) 用outerNormal

try——>catchBody 将执行:()=>C(outerNormal,outerError)即为::()=>C(()=>D(qNormal, qError),outerError)

而对于try体,则有:(bodyNormal, bodyError)=>A(x=>B(x, bodyNormal, bodyError), bodyError);

因此,展开就为:

A(
x=>B( // A's normal continuation
x, // B's argument
()=>D( // B's normal continuation
qNormal, // D's normal continuation
qError), // D's error continuation
()=>C( // B's error continuation
()=>D( // C's normal continuation
qNormal, // D's normal continuation
qError)
, // D's error continuation
qError)), // C's error continuation
()=>C( // A's error continuation
()=>D( // C's normal continuation
qNormal, // D's normal continuation
qError), // D's error continuation
qError)) // C's error continuation

如果C抛出异常则,continuation立刻被转入qError执行;

从可读性上来讲这当然不是一种非常好的实现方式。但这是一种思路,我们在编写一些前后关联性很强的方法调用时,并且方法调用不是很多时,可以采用这种做法。

下面的实现很好地体现了CPS的流式调用(伪递归)和数据处理控制权的交接:

实现Factorial:

static void Main()
{
    Factorial(5, x => Console.WriteLine(x));
}

static void Factorial(int n, Action<int> k)
{
    if (n == 0)
        k(1);
    else
        Factorial(n - 1, x => k(n * x));
}
当然CPS还有一个很有用的特性就是能够支持异步回调,在异步编程中很有用。



 
  

原文发布时间为:2011-02-24

本文作者:vinoYang

本文来自云栖社区合作伙伴CSDN博客,了解相关信息可以关注CSDN博客。

目录
相关文章
|
算法
数学建模-------误差来源以及误差分析
数学建模-------误差来源以及误差分析
|
网络协议 关系型数据库 MySQL
网络编程01 常见名词的一些解释
网络编程01 常见名词的一些解释
|
监控 虚拟化 Docker
Window系统安装Docker
这篇文章提供了在Windows系统上安装与配置Docker的详细指南,包括Docker Desktop的安装及通过Docker来运行容器化应用的步骤。
678 2
Window系统安装Docker
|
数据采集 Linux Go
Linux系统是如何控制串口收发数据的?
Linux系统是如何控制串口收发数据的?
335 0
|
运维 网络协议 网络安全
2023网络建设与运维正式赛卷-IP地址分配-上
该集团进行数字化转型,构建了包括总公司两个数据中心、异地灾备数据中心的“两地三中心”架构,使用OSPF、RIP、ISIS、BGP路由协议互联。核心交换机SW1、SW2、SW3及分公司的AC1配置了多个VLAN和Loopback接口,以IPv4和IPv6双栈进行地址规划。SW1和SW2的VLAN1024配置了VRF Finance,用于财务应用。所有设备均需配置以确保网络稳定、安全和可扩展,并通过客户端测试验证应用访问。
2023网络建设与运维正式赛卷-IP地址分配-上
|
JavaScript API 数据安全/隐私保护
vue3制作类微信的六位的密码输入框
vue3制作类微信的六位的密码输入框
438 0
|
SQL Oracle 关系型数据库
DataGrip初体验
DataGrip总体来说是一款非常好的数据库管理软件,整体风格和Idea一样,智能代码补写非常好用。
1840 1
|
Web App开发 SQL Java
Linux面试最高频的5个基本问题
提到CPU利用率,就必须理解时间片。什么是CPU时间片?我们现在所使用的Windows、Linux、Mac OS都是“多任务操作系统”,就是说他们可以“同时”运行多个程序,比如一边打开Chrome浏览器浏览网页还能一边听音乐。
|
开发框架 算法 NoSQL
.NET服务治理之限流中间件-FireflySoft.RateLimit2
.NET服务治理之限流中间件-FireflySoft.RateLimit2
288 0