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

Reactive Extensions 入門(1)

.net

入門するのは私なんですけどね。.NET Framework 4 SP1 で追加されると噂の Reactive Extensions(以下 Rx)。ずっと気にはなっていたんですが、まったく触っていませんでした。


id:okazuki さんが Rx の入門記事を書いていたり、neue さんが Rx の詳細な解説を書いていたりしていて、今更な感じがしますが、ブログのネタに困っていたので記事にしてみます。車輪の再発明みたいなもんです。あと、コード書かないと理解できない残念な頭なので、記憶の整理も兼ねています。


Rx と言えば、仮面ライダー…じゃなかった、Observer パターンです。あの GoFデザインパターンのひとつ。

簡単に言えば Rx は「Observer パターンを実装し、さらに変態的に拡張しまくったライブラリ」ってところです。


Rx の中心に、IObserver インタフェースと IObservable インタフェースがあります。Observer パターンで使うもので、IObservable が送信側、IObserver が受信側です。このインタフェースを使って、簡単な Observer パターンを実装してみます。

using System;
using System.Collections.Generic;

namespace RxSample1
{
    partial class Program
    {
        static void Main(string[] args)
        {
            // 通知を出すオブジェクト
            var ob = new FooObservable();

            // 通知先のオブジェクトと登録
            ob.Subscribe(new FooObserver());

            // 通知を出す
            ob.Notify("Hello");
            ob.Notify("World");

            Console.ReadLine();
        }
    }

    // 通知を出すクラス
    public class FooObservable : IObservable<string>
    {
        // 通知先のオブジェクトを格納
        private List<IObserver<string>> _observers = new List<IObserver<string>>();

        // 通知先のオブジェクトを登録
        public IDisposable Subscribe(IObserver<string> observer)
        {
            _observers.Add(observer);
            return null;
        }

        // 登録されているオブジェクトに通知を出す
        public void Notify(string value)
        {
            foreach (var o in _observers)
            {
                o.OnNext(value);
            }
        }
    }

    // 通知を受けるクラス
    public class FooObserver : IObserver<string>
    {
        public void OnCompleted() { }
        public void OnError(Exception error) { }

        // 通知を受けて何かする
        public void OnNext(string value)
        {
            Console.WriteLine("FooObserver: {0}", value);
        }
    }
}

IObservable や IObserver を毎回実装するのは面倒ですよね。そう言われることを予想したのか、2つのインタフェースを実装した Subject という便利クラスが用意されています。このクラスを使って、先のコードと同じことをやるとこんな感じ。

using System;
using System.Collections.Generic;

namespace RxSample2
{
    partial class Program
    {
        static void Main(string[] args)
        {
            // IObservable<T> と IObserver<T> を実装したクラスが提供されている
            var sbj = new Subject<string>();

            // 通知先は関数を登録するだけでいい
            // 戻り値のオブジェクトは登録を解除するときに使う
            var d = sbj.Subscribe(s => Console.WriteLine("Subject: {0}", s));

            // IObserver<T> を実装したクラスを登録することもできる
            sbj.Subscribe(new FooObserver());

            // 通知を出す
            sbj.OnNext("Foo");

            // 登録を解除
            d.Dispose();

            // 再び通知を出す
            // 「OnNext2:〜」は出力されないはず
            sbj.OnNext("Bar");

            Console.ReadLine();
        }
    }
}

Suject クラスでは、通知を送信するときに OnNext を使っています。ここちょっと混乱しますね。実際に Rx を使うときは、Subject ではなく Observable というもっと便利なクラスを使うことが多いので、気にする必要は無いのかな。次回はその Observable クラスを触ってみます。


あと、MacBook Air 11インチ欲しい!