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

RE:ADV

UnityでADVを作るブログ

コンソールアプリケーションで学ぶReactiveExtensionの仕組み(チュートリアル作ったよ)

なんとなくRxよさそうだな、もっと勉強したい!という欲が高まってきたのですが、Unityをいきなり絡めると決まりごとが増えるので(MonoBehaviourのUpdate()の仕組みとかCoroutioneとか)、単純さを前面に押し出した勉強法を探していました。

単純!基本!といえばコンソールアプリケーションです。

僕も2014年からC#勉強し始め組として、VisualStudioを使ったコンソールアプリケーションでの勉強法はプログラム習得にとても有効だったと思っています。

ReactiveExtension(Rx)を学ぶのにも、大量のコンソールアプリケーションのサンプルがあればいいなぁと思いました。それで探してみると、ちょうどよさそうなのがありました。
Rx Wiki - Reactive Framework (Rx) Wiki
です。

このサイトのサンプルをもとにチュートリアルを作ってみたいと思います。

まず、最初のページの以下のリンク 101 Rx Samples をクリックします。飛んだページのTable of Contentsから、サンプルコードを選んでいきます。

今回は一番下の

Use Subject<T> as backend for IObservable<T>

(訳:IObservable<T>のバックエンドとしてのSubject<T>)
をやってみたいと思います。

このSubject<T>がすごく便利で、今後頻出することが予想されます。
(Subject<T>は一言で言うと、IObserver<T>とIObservable<T>どちらも実装したクラス)

元のコードはこれ↓
f:id:aktaat:20141227171505p:plain

orderクラスはプライベートなフィールド_paidDateを持ちます。(DateTime?はヌル許容型)
次にプライベートなストリーム、order型のサブジェクト_paidSubjをnew演算子で作ります。
かつその_paidSubjをAsObservable()して返すゲッターを設定。(これで他のクラスに公開されます)

メソッドMarkPaid()は_paidDateを引数から設定し、_paidSubjストリームに自分自身(order)をOnNext()しています。
Subject<T>ストリームはIObserver<T>を実装しているのでOnNext()が可能なんですね。OnNext()することでストリームに値が流れます。ここでのポイントは流れている値の型が<order>ということです。これについては後ほどいじってみたいと思います。

VisualStudioを立ち上げ、C#の新しいプロジェクトからコンソールアプリケーションを作成します。
f:id:aktaat:20141227180024p:plain

ReactiveExtension(Rx)を参照するライブラリに追加しなければなりません、ので、NuGetを使います。
f:id:aktaat:20141227180550p:plain
ソリューション・エクスプローラーの[ConsoleApprlication57]を右クリック。
[Manage NuGet Packages...]を選択します。
すると以下のような画面が出るので、
f:id:aktaat:20141227180843p:plain
検索窓で reactive と入れてやると一番上に目的のライブラリが出てきますので、インストールします。途中で何か聞かれたらacceptしてずんずん進んでください。

終わったら、using 名前空間に追加することでReactiveExtensionが使えるようになりました。
f:id:aktaat:20141227181240p:plain

サンプルコードの通り、打ち込んでいきます。
f:id:aktaat:20141227181426p:plain
これを実行すると…
f:id:aktaat:20141227181525p:plain
無事動きました。

Main関数の中で、新しいorderオブジェクトを作成し、Paidストリームが値を受け取ったときの挙動をSubscribe()メソッド(内のラムダ式)で定義しています。
サンプルでは、Paidというストリング型の値を表示していますね。
最後にMarkPaid()メソッドを実行して値を流しています。

ここでふと思うのは、<order>型のオブジェクトを流しているのに、出力は関係ないストリングっていうのはあまりおもしろくないな、ということです。
MarkPaid()メソッドはDateTimeを引数に持っているので、その値を出力できないか?
というわけでやってみました。
f:id:aktaat:20141227182339p:plain
実行。
f:id:aktaat:20141227182531p:plain
うん。意図したとおりに動いています。

追加・変更したのは2点。自身のメンバフィールド_paidDateにゲットのプロパティをつけて…

public DateTime? PaidDate{ get { return _paidDate; } }

サブスクライブの内容を、プロパティからの値の取得に書き換えます。

order.Paid.Subscribe(_ => Console.WriteLine("{0}",order.PaidDate));//Subscribe

オブジェクトをストリームに流して、意図したメンバの値を取得するという、このサンプルプログラムのストーリーはかなり使えるのではないかなーと思います。
ぜひ試してみてください。