許多模型都可以統(tǒng)一至標(biāo)準(zhǔn)的生產(chǎn)者接口IObservable上,如MouseMove事件便可以認(rèn)做是“永不停止的MouseEventArgs對象的生產(chǎn)者”。而另一方面,單個(gè)異步操作則可以被視為“只產(chǎn)出單個(gè)數(shù)據(jù)便結(jié)束的生產(chǎn)者”。
LINQ to Observable
在許多人眼中,C# 3.0中新增的LINQ特性只是一種用于操作數(shù)據(jù)的DSL,它的主要作用也僅僅是針對IEnumerable或是IQueryable的數(shù)據(jù)操作。
事實(shí)上,LINQ本身的能力遠(yuǎn)不止此。LINQ是一種非常簡單的語言特性,編譯器只是將LINQ查詢語句轉(zhuǎn)化為“字面等價(jià)”的“LINQ標(biāo)準(zhǔn)方法”調(diào)用(如Where,Select等等)和“和Lambda表達(dá)式”參數(shù)(如x => x > 0)而已。但是這里的關(guān)鍵便是“字面等價(jià)”四個(gè)字,LINQ本身并不規(guī)定“LINQ標(biāo)準(zhǔn)方法”是對象的實(shí)例方法還是擴(kuò)展方法,“Lambda表達(dá)式”是構(gòu)造出一個(gè)匿名函數(shù)還是表達(dá)式樹,這一切都是由利用LINQ的類庫來決定的。因此,微軟能夠基于LINQ實(shí)現(xiàn)了PLINQ這樣的并行類庫。直到最近的訪談中,LINQ的設(shè)計(jì)者Erik Meijer依舊認(rèn)為LINQ還是被低估了,他們自己也還在繼續(xù)挖掘LINQ的更多能力。
雖然LINQ本身在語法上只是一些方法調(diào)用,但是它在語義上是針對數(shù)據(jù)流的一系列操作,因此LINQ to Object,LINQ to SQL以及PLINQ可以認(rèn)為是最自然,最符合LINQ語義的應(yīng)用。如今,微軟的“云編程能力團(tuán)隊(duì)(Cloud Programmability Team)”在其“響應(yīng)式框架(Reactive Framework,簡稱Rx)”提供了LINQ to Observable,這又是另一個(gè)LINQ的經(jīng)典使用案例。IObservable本身可以被認(rèn)為是一股“推送數(shù)據(jù)”的數(shù)據(jù)流,因此也可以對其進(jìn)行“過濾”或是“投影”等操作,這便是LINQ的應(yīng)用場景,LINQ to Observable是對IObservable接口實(shí)現(xiàn)的一系列LINQ標(biāo)準(zhǔn)方法。因此,我們可以認(rèn)為,LINQ to Observable是一套與LINQ to Object對偶的類庫,事實(shí)上在響應(yīng)式框架中,還有一套與LINQ to Queryable對偶的LINQ to Qbservable,請注意第一個(gè)字母是Q,我們可以把它看作是LINQ to Observable的“遠(yuǎn)程查詢”版本。
利用LINQ to Object可以編寫出聲明式(表示“做什么”)的代碼,其可讀性往往遠(yuǎn)高于等價(jià)的命令式(表示“怎么做”)代碼。LINQ to Observable也有類似的效果。假設(shè)現(xiàn)在有一個(gè)需求:利用ADWS鍵控制小球的位置。傳統(tǒng)的寫法可能是這樣的:
void OnKeyPress(object sender, KeyPressEventArgs e) { // 如果游戲已經(jīng)開始 if (isPlaying) { // 向左且小球沒有超出邊界 if (e.KeyChar == 'a' && ball.Left > 0) { ball.Left -= 5; } // 向上且小球沒有超出邊界 else if (e.KeyChar == 'w' && ball.Top > 0) { ball.Top -= 5; } else ... } else ... }
由于KeyPress事件總是不斷觸發(fā),因此我們只能它的事件處理器中進(jìn)行判斷各種狀態(tài),采取不同措施。而如果我們利用LINQ to Observable,則幾乎是另外一種思維方式:
// 過濾出isPlaying時(shí)的keyPress事件 var keyPress = GetKeyPress().Where(_ => isPlaying); // 過濾出向左移動(dòng)的事件 var moveLeft = from ev in keyPress where ev.EventArgs.KeyChar == 'a' where ball.Left > 0 select ev; moveLeft.Subscribe(_ => ball.Left -= 5); // 過濾出向上移動(dòng)的事件 var moveTop = from ev in keyPress where ev.EventArgs.KeyChar == 'w' where ball.Top > 0 select ev; moveTop.Subscribe(_ => ball.Top -= 5);
我們可以將“KeyPress事件”視為“推送KeyPressEventArgs對象”這一數(shù)據(jù)流的數(shù)據(jù)源(由GetKeyPress方法返回),那么如今的代碼便是使用LINQ過濾出“需要”的數(shù)據(jù),并針對真正需要的那部分進(jìn)行響應(yīng)。這么做,便將“條件”與“操作”解耦,顯著增強(qiáng)了代碼的語義表達(dá)能力。事實(shí)上,只要補(bǔ)充一些輔助方法,可以利用LINQ表示更為完整復(fù)雜的邏輯。例如,微軟咨詢師Matthew Podwysocki便在博客中展示過一段代碼,基于LINQ to Observable實(shí)現(xiàn)了創(chuàng)建一個(gè)WebRequest對象,設(shè)置屬性,異步發(fā)送及下載數(shù)據(jù)的一系列操作。
出處:老趙點(diǎn)滴
責(zé)任編輯:bluehearts
上一頁 異步編程與響應(yīng)式框架 [2] 下一頁 異步編程與響應(yīng)式框架 [4]
◎進(jìn)入論壇網(wǎng)絡(luò)編程版塊參加討論
|