LoginSignup
7

More than 5 years have passed since last update.

C#とRxでリアクティブプログラミングしてみた。

Last updated at Posted at 2016-02-12

※ C#初心者なので、コードで至らない点ありましたコメント頂けたら助かります。

なんだそれ

Rx

Reactive Extensionsというライブラリ。
簡単に紹介すると、Linqライクな構文を持つ 特定のシーケンス の管理が出来るもの (と認識している。)

詳細はググって下さい。

リアクティブプログラミング

結構、抽象的な概念だったりするので、私からの説明は割愛。
RP(Reactive Programming) や FRP(Functional Reactive Programming)

などがあります。
それを区分けするには、未だ理解度が足りない。

詳細はググって下さい。

開発環境の準備

私が使用している端末はMacなので Xamarin Studio というIDEを使う事にしました。
VisualStudio や MonoDevelopなどでも同様な事が出来ると思います。

C#のコンソールアプリケーションを作り、add packagesからズドンと
Reactive Extesions - Main Library
を突っ込みます。
スクリーンショット 2016-02-12 21.05.35.png

早速コードを書こう。

シーケンス管理と言う事で、 私の休日のシーケンスを、リアクティブプログラミングで表現しました。

ドスン

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

namespace Rx
{
    //  ステータス管理型
    using StateMap = Dictionary<string, Dictionary<string, Action>>;
    class MainClass
    {
        public static void Main (string[] ars)
        {
            //  ステータス定義
            const string AWAKE      = "AWAKE";
            const string EAT        = "EAT";
            const string NET_SURFIN = "NET_SURFIN";
            const string SLEEP     = "SLEEP";

            //  ステータス上で使われる、呼び出し関数の想定
            const string ON_ENTER   = "onEnter";
            const string UPDATE     = "update";
            const string ON_EXIT    = "onExit";

            var stateMap = new StateMap ();

            //  初期化用delegate、仮でステート名と呼び出し関数名を表示する。
            //  Updateでsleepをしているのは、処理してる雰囲気出すため。意味は無い。
            Action<string, StateMap> stateInit = delegate(string stateStr, StateMap obj) {
                var action = new Dictionary<string, Action>();
                action.Add(ON_ENTER, () => Console.WriteLine(stateStr + ":" + ON_ENTER));
                action.Add(ON_EXIT, () => Console.WriteLine(stateStr + ":" + ON_EXIT));
                action.Add(UPDATE, delegate {
                    Console.WriteLine(stateStr + ":" + UPDATE);
                    System.Threading.Thread.Sleep (1000);
                });
                obj.Add(stateStr, action);
            };

            //  各種ステートで実際に初期化
            stateInit (AWAKE     , stateMap);
            stateInit (EAT       , stateMap);
            stateInit (NET_SURFIN, stateMap);
            stateInit (SLEEP     , stateMap);

            //  Rxにステート毎のシーケンスを登録します。
            IObservable<Action> provider = Observable.Create<Action> (obj => {
                foreach (var state in stateMap) {
                    obj.OnNext (() => state.Value[ON_ENTER]());
                    obj.OnNext (() => state.Value[UPDATE]());
                    obj.OnNext (() => state.Value[ON_EXIT]());
                }
                obj.OnNext (() => Console.WriteLine ("all sequence success."));
                obj.OnCompleted ();
                return System.Reactive.Disposables.Disposable.Empty;
            });

            //  実行!
            provider.Subscribe (sequence => sequence ());
        }
    }
}

AWAKE:onEnter
AWAKE:update
AWAKE:onExit
EAT:onEnter
EAT:update
EAT:onExit
NET_SURFIN:onEnter
NET_SURFIN:update
NET_SURFIN:onExit
SLEEP:onEnter
SLEEP:update
SLEEP:onExit
all sequence success.

何故か、哀しくなりますね。

こういう使い方が正しいのかどうか分かりませんが、ステートマシンの代わりになるってだけで良いのではないでしょうか。

まだ試していませんが、各種EventやwebAPIの通信などでも、Rxを使えば 一つのシーケンスとして 実装が出来るそうです。
すごい。

脱・コールバックネスト地獄!

勉強中なので今回はここまで。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7