Reactive Extensions 入門(4)

Observable クラスは IObservable を生成するいろんな機能を提供しています。その中でちょっと変わっているのが、C# のイベントを IObservable に変換するもの。さっそく試してみます。


まずは下準備。イベントを発生させるクラスを用意します。

public class GreetedEventArgs : EventArgs
{
    public string Name { get; private set; }
    public GreetedEventArgs(string name)
    {
        Name = name;
    }
}

public class Greeting
{
    public event EventHandler<GreetedEventArgs> Greeted;
    public void Greet(string name)
    {
        var h = Greeted;
        if (h != null)
        {
            h(this, new GreetedEventArgs(name));
        }
    }
}


イベントを IObservable に変換するには、FromEvent メソッドを使います。

var g = new Greeting();
Observable.FromEvent<GreetedEventArgs>(g, "Greeted")
    .Subscribe(e => Console.WriteLine("Hello,{0}.", e.EventArgs.Name));
g.Greet("悟空");

// [実行結果]
// Hello,悟飯.

この例の場合、イベントを発生させるオブジェクトとイベント名を渡すと、イベント引数を通知する IObservable に変換されます。Subscribe で渡しているデリゲートがイベントハンドラです。


ただ、私はイベント名を文字列で指定するのが嫌。すごく嫌です。そんな型安全至上主義な人のために、タイプセーフな書き方も提供されています。

var g = new Greeting();
Observable.FromEvent<GreetedEventArgs>(
    h => g.Greeted += h,
    h => g.Greeted -= h)
    .Subscribe(e => Console.WriteLine("Hello,{0}.", e.EventArgs.Name));
g.Greet("ベジータ");

// [実行結果]
// Hello,ベジータ.


イベントを IObservable に変換して何が嬉しいかというと、LINQ でおなじみの拡張メソッドを使って、イベントデータを操作できることでしょうか。試しに Where でフィルタリングしてみたのがこちら。

var g = new Greeting();
Observable.FromEvent<GreetedEventArgs>(g, "Greeted")
    .Where(e => e.EventArgs.Name == "フリーザ")
    .Subscribe(e => Console.WriteLine("Hello,{0}.", e.EventArgs.Name));
g.Greet("バーボン");
g.Greet("ドドリア");
g.Greet("フリーザ");

// [実行結果]
// Hello,フリーザ.


もちろん、タイプセーフな書き方のときもイベントデータを操作できます。フィルタリングはさっきやったので、今度は Select で変換してみます。

var g = new Greeting();
Observable.FromEvent<GreetedEventArgs>(
    h => g.Greeted += h,
    h => g.Greeted -= h)
    .Select(e =>
    {
        if (e.EventArgs.Name == "悟空")
        {
            return "カカロット";
        }
        else
        {
            return e.EventArgs.Name;
        }
    })
    .Subscribe(s => Console.WriteLine("Hello,{0}.", s));
g.Greet("悟空");
g.Greet("悟飯");
g.Greet("ベジータ");

// [実行結果]
// Hello,カカロット.
// Hello,悟飯.
// Hello,ベジータ.


今回の試したのは Where と Select だけですが、使える拡張メソッドは大量にあります。いろいろ応用できそうです。もう、夢が広がりまくりんぐ。イベントも LINQ で処理する時代がきそうですね。