Reactive Extensions介紹

張善友發表於2013-08-02

Reactive Extensions(Rx)是對LINQ的一種擴充套件,他的目標是對非同步的集合進行操作,也就是說,集合中的元素是非同步填充的,比如說從Web或者雲端獲取資料然後對集合進行填充。Rx起源於Microsoft DevLabs小組的研究,他擴充套件了LINQ的一些特性,目前Rx支援多種平臺如JavaScript,Windows Phone,ios,Android 。隨著資料處理變得複雜,LINQ使得我們的處理邏輯變得簡單清晰,同樣地,隨著越來越多的資料通過從雲端非同步獲取,Rx使得這種非同步資料處理操作變得簡單和容易維護。

在處理靜態集合資料方面,LINQ使用類似SQL的語法來操作和使用不同來源的資料。相反,Rx被設計出來用來處理將來才會填充好的集合,也就是說,集合型別定義好了,但是集合中的元素可能在未來的某一時刻才會被填充。

LINQ和Rx在技術上有很多相似的地方。在LINQ對集合進行一系列操作如新增,移除,修改,提取後,會得到一個新的集合,新集合只是原始集合的一個修改版本。Rx也是一樣,集合和資料流看起來非常不同,但是他們在很多關鍵的地方有聯絡,這就是我們將資料流稱之為未來的集合的原因。集合和資料流都是多資料按某種順序進行排列。LINQ和Rx可以這些序列進行一系列操作然後得到一個新的序列。

Rx提供了一種新的組織和協調非同步事件的方式,例如協調多個從雲端返回的多個非同步的資料流。Rx能夠是的我們用一個簡單的方式來處理這些資料流,極大的簡化了程式碼的編寫。例如,.NET中傳統的Begin/End非同步程式設計模式在處理單個非同步操作時可以應付,但是如果同時多個非同步呼叫時,執行緒控制就會使得程式碼變得比較複雜。使用Rx,Begin/End模式就變成了一條簡單的方法,這使得程式碼更加清晰和容易理解。

    Rx最顯著的特性是使用可觀察集合(Observable Collection)來達到整合非同步(composing asynchronous)和基於事件(event-based)的程式設計的效果。Rx有一些幾個特性。

  • 組合(Composing): Reactive Extension的首要目標之一就是將多種非同步操作組合起來是的程式碼更加簡單。要做到這一點,資料流必須定義清楚,這樣程式碼就很清晰集中,使得非同步操作程式碼非同步處理程式碼不會充斥整個應用程式。
  • 非同步(Asynchronous): 雖然Rx不僅僅能處理非同步操作,但是使用Rx,大大簡化了非同步操作的實現,並且程式碼容易理解進而容易維護。
  • 基於事件(Event-based): Rx簡化了傳統的非同步程式設計方式
  • 可觀察集合(Observable collections): Obervable Collection是Rx的核心,它是一種集合,集合的元素在第一次訪問的時候肯能還沒有填充。它對與Rx的重要性類始於enumerable集合對LINQ的重要性。

下面來看看一個簡單的例子來說明Rx的用法:

新建一個工程RxDemo,通過Nuget 獲取Rx的最新版本:

image

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reactive.Linq;
using System.IO;

namespace RxDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            //使用Range方法返回Observable集合
            IObservable<Int32> input = Observable.Range(1, 15);
            input.Where(i => i % 2 == 0).Subscribe(x => Console.Write("{0} ", x));
            Console.WriteLine();

            //使用Array返回Observabale集合
            var myArray = new[] { 1, 3, 5, 7, 9 };
            IObservable<Int32> varmyObservable = myArray.ToObservable();
            varmyObservable.Subscribe(x => Console.WriteLine("Integer:{0}", x));
            Console.WriteLine();
            //Take操作符,用來指定獲取集合中的前幾項
            var take = new[] { 1, 2, 3, 4, 5, 4, 3, 2, 1 }.ToObservable();
            take.Take(5).Select(x => x * 10).Subscribe(x => Console.WriteLine(x));
            Console.WriteLine();
            //Skip操作符表示跳過集合中的n條記錄。
            var skip = new[] { 1, 2, 3, 4, 5, 4, 3, 2, 1 }.ToObservable();
            skip.Skip(6).Select(x => x * 10).Subscribe(x => Console.WriteLine(x));
            Console.WriteLine();
            //Distinct操作符用來去除集合中的非重複資料。
            var distinct = new[] { 1, 2, 3, 4, 5, 4, 3, 2, 1 }.ToObservable();
            distinct.Distinct().Select(x => x * 10).Subscribe(x => Console.WriteLine(x));
            //Rx也需要釋放資源
            Console.WriteLine();
            var ObservableStrings = Observable.Using<char, StreamReader>(
                () => new StreamReader(new FileStream("randomtext.txt", FileMode.Open)),
                streamReader => (streamReader.ReadToEnd().Select(str => str)).ToObservable()
                );
            ObservableStrings.Subscribe(Console.Write);
            Console.WriteLine();
            //在Rx中Zip是將兩個Observable物件合併為一個新的Observable物件。
            var numberCitys = varmyObservable.Zip(input, (range, array) => range + ":" + array);
            numberCitys.Subscribe(Console.WriteLine);
            Console.ReadKey();
        }
    }
}

上述程式碼使用Observable.Range返回個生產Observable物件,他和Enumerable物件的Range方法含義類似,該方法接受兩個引數,一個開始值,以及產生值的個數。

Mono 3.2已經包含了Rx框架,我們的程式碼都是Mono中執行的,看下效果:

image

Rx中的一些操作符和LINQ操作符有很多功能是相同的。下面對最常用的take,skip,distinct,using和zip這個操作符進行說明。

Take

Rx中的Take操作符和LINQ中的功能一樣,它用來指定獲取集合中的前幾項。

Skip

Skip語句表示跳過集合中的n條記錄。這在有些情況下非常有用,比如解析文字的時候,可能第一行是表頭,所以可以使用skip跳過第一行,從第二行開始讀取。還有就是在分頁的時候和take一起使用非常方便。

Distinct

Distinct用來去除集合中的非重複資料。

Using

Rx也需要清理資源,當使用到了一些受限制資源或者非託管資源時,需要我們去管理這些資源的釋放。

當然,我們可以呼叫Observable物件的一個稱之為Using的靜態方法。方法返回一個IObservable<char>型別物件,接受兩個引數,第一個引數是一個返回StreamReaderde的Func型別引數,第二個是一個接受第一Func引數返回的StreamReader物件,返回一個型別為char的IObservable集合。

Zip

    和LINQ中的Zip操作類似。LINQ中的Zip是將兩個集合合併為一個新的集合,在Rx中Zip是將兩個Observable物件合併為一個新的Observable物件。

 

Reactive Extensions入門