読者です 読者をやめる 読者になる 読者になる

雑記 - otherwise

最近はDQ10しかやっていないダメ技術者がちまちまと綴る雑記帳

わんくま東京勉強会 #64 アフターフォロー (16) : 通信処理 - Reactive Extensions for .NET (Rx)

WindowsPhone わんくま

Windows Phone での通信処理は 基本的に Silverlight と変わりなく行えます。
WCF Data Services も利用可能なので、利用可能なパターンでは積極的に使うと良いと思います。

非同期処理

Silverlight と同じ、と云う事で、 Windows Phone の通信処理は非同期で処理されます。

var wq = WebRequest.CreateHttp("http://example.jp/foo");
wq.BeginGetResponse(ar =>
    {
        var res = wq.EndGetResponse(ar);
        var value = new StreamReader(res.GetResponseStream()).ReadToEnd();
        Dispatcher.BeginInvoke(() => MessageBox.Show(value));
    }, null);

単発の呼び出しであればそれ程でもないですが、連続して呼び出したり複数呼び出しの結果を受けて後続処理を実行する等となると、エラー処理も考慮するとネストの嵐になってとても管理出来る状態ではなくなってしまいます。

// 下記コードはエラー処理をしていません。。。
var req = WebRequest.CreateHttp("http://example.jp/foo");
req.BeginGetResponse(ar =>
    {
        var res = req.EndGetResponse(ar);
        var value = new StreamReader(res.GetResponseStream()).ReadToEnd();

        var req2 = WebRequest.CreateHttp("http://example.jp/bar");
        req2.BeginGetResponse(ar2 =>
            {
                var res2 = req2.EndGetResponse(ar2);
                var value2 = new StreamReader(res2.GetResponseStream()).ReadToEnd();

                var req3 = WebRequest.CreateHttp("http://example.jp/baz");
                req3.BeginGetResponse(ar3 =>
                    {
                        var res3 = req3.EndGetResponse(ar3);
                        var value3 = new StreamReader(res3.GetResponseStream()).ReadToEnd();
                        Dispatcher.BeginInvoke(() => MessageBox.Show(value + value2 + value3));
                    }, null);
            }, null);
    }, null);

この問題を緩和する手段として、 Reactive Extensions for .NET (Rx) が有用です。
特に Windows Phone はコアランタイムに Rx のライブラリが含まれている為、特に追加モジュールを導入する事なく Rx を使用する事が出来るので、非同期処理には基本的に Rx を使用する事をお薦めします。

var req = WebRequest.Create("http://example.jp/foo");
var req2 = WebRequest.Create("http://example.jp/bar");
var req3 = WebRequest.Create("http://example.jp/baz");
Observable.Merge(
    Observable.FromAsyncPattern<WebResponse>(req.BeginGetResponse, req.EndGetResponse)(),
    Observable.FromAsyncPattern<WebResponse>(req2.BeginGetResponse, req2.EndGetResponse)(),
    Observable.FromAsyncPattern<WebResponse>(req3.BeginGetResponse, req3.EndGetResponse)())
    .Select(res => new StreamReader(res.GetResponseStream()).ReadLine())
    .ObserveOnDispatcher()
    .Subscribe(v =>
    {
        // 結果を処理
    });
// 1 つ目のレスポンスが 2 つ目の URL である場合なら、以下の様に書けます。
var req = WebRequest.Create("http://example.jp/foo");
Observable.FromAsyncPattern<WebResponse>(req.BeginGetResponse, req.EndGetResponse)
    .Select(res =>
    {
        var req2 = WebRequest.Create(new StreamReader(res.GetResponseStream()).ReadToEnd());
        return Observable.FromAsyncPattern<WebResponse>(req2.BeginGetResponse, req2.EndGetResponse);
    })
    .Select(res => new StreamReader(res.GetResponseStream()).ReadToEnd())
    .ObserveOnDispatcher()
    .Subscribe(v =>
    {
        // 結果を処理
    });

参照 DLL

Windows Phone の Rx を利用するには Microsoft.Phone.Reactive と System.Observable のDLL への参照設定を追加する必要があります。(どちらもコアランタイムの一部なので xap ファイルに含める必要はありません)

Windows Phone の Rx の注意点

Windows Phone 7.0 の RTM の時点で Rx 自体が Beta だったと云う事もあり、現在の Rx 本体と Windows Phone 同梱の Rx ではインターフェースに差異があります。
Rx の情報をネットで検索する際はその点を注意する様にしてください。
なお、最新版の Rx を外部モジュールで追加して利用する事も出来ます。
色々と追加された機能もあるので、ガッツリ Rx を利用したい方はそちらをおススメします。
Windows Phone 向けの Rx の最新版は Data Developer Center からダウンロード出来ますし、 NuGet からも導入可能です。