如果你還不是很瞭解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還有一個很有用的特性就是能夠支援非同步回調,在非同步編程中很有用。