.NET Reactive Framework メソッド探訪第一回:FromEvent


InfoQ: .NETRxLINQ to Events.NET 4.0()Silverlight Toolkitunfold: Introducing Rx (Linq to Events)


Buried deep in the bin folder of the Silverlight Toolkit Unit Tests is a hidden gem: The Rx Framework (System.Reactive.dll). If you glanced quickly youd miss it altogether but its one of the most exciting additions to the .NET framework since Linq.


(Parallel使)4.0Linq to ObjectsC#3.0foreachLinq.NET4.0LinqLinq to Everywhere! Functional Reactive Programming!

How to use


Silverlight ToolkitSource/Binaries/System.Reactive.dllSilverlightgithub(Cecildll)SilverlightSilverlight 3 Tools



Silverlight15 = 15 = WhereSelect
// XAMLではなく全部コード上に書いたのは両方を張るのが面倒だから……
// 内容はCanvasとEllipseを配置するというもので、本筋とは関係ありません
InitializeComponent();
var canvas = new Canvas { Background = new SolidColorBrush(new Color {A=255, R = 100, G = 100, B = 100 }) };
var ellipse = new Ellipse { Height = 30, Width = 30, Fill = new SolidColorBrush(Colors.Orange) };
canvas.Children.Add(ellipse);
this.Content = canvas;

// FromEventはイベント発火がトリガとなってLinq発動
// 後段に送られるのはEvent<T>というもので、
// SenderとEventArgsという読み取り専用プロパティを持つクラス
var canvasMove = Observable.FromEvent<MouseEventArgs>(canvas, "MouseMove")
    .Select(e => e.EventArgs.GetPosition(canvas))
    .Where(p => (p.X % 15 == 0) || (p.Y % 15 == 0))
    .Subscribe(p =>
    {
        ellipse.SetValue(Canvas.LeftProperty, p.X - ellipse.Width / 2);
        ellipse.SetValue(Canvas.TopProperty, p.Y - ellipse.Height / 2);
    });

// Subscribeの戻り値の型はIDisposable
// Disposeを呼ぶと登録したイベントをデタッチすることが出来る
// デタッチしないなら取得する必要は特にはない
// canvasMove.Dispose();

MouseMoveLinqLinqObserverIteratorObservable.FromEventPushIObservable<T>Event<T>使sendereventArgsIObservable(Select, Where, TakeWhileDelay, WaitUntil)SubscribeSubscribeLinqForEachSubscribe
// つまるところ、以下のコードと同じだったりはする
// ただ、IObservable<T>は通常のイベント登録では無理な複雑な操作が簡単、
// そして何よりも、このような単純なコードでもそんなに複雑になっていない!
canvas.MouseMove += (sender, e) =>
{
    var pos = e.GetPosition(canvas); // Select
    if (!(pos.X % 15 == 0 || pos.Y % 15 == 0)) return; // Where
    ellipse.SetValue(Canvas.LeftProperty, pos.X - ellipse.Width / 2);
    ellipse.SetValue(Canvas.TopProperty, pos.Y - ellipse.Height / 2);
};

FromEventMouseEventArgsIObservableFromEvent()
// stringを避けたこういう登録方法もあるけれど、面倒なうえに警告出る
Observable.FromEvent((EventHandler<MouseEventArgs>h) => new MouseEventHandler(h),
        h => canvas.MouseMove += h, // addHandler
        h => canvas.MouseMove -= h) // removeHandler
    .Subscribe(e => Debug.WriteLine(e.EventArgs.GetPosition(canvas)));

stringJavaScriptaddEventHandlerIntelliSenseFromEventEventhandler<MouseEventArgs>MouseMoveMouseEventHandler()addremovestring

// 普段あまり書かないMouseEventArgsとかいう型定義は書きにくいし
// メソッド名もstringで書くのはミスが出がち、ということで
// 拡張メソッドでイベント取り出し用のメソッドを予め作っておくと良い
public static IObservable<Event<MouseEventArgs>> GetMouseMove(this UIElement elem)
{
    return Observable.FromEvent<MouseEventArgs>(elem, "MouseMove");
}

// マウスの軌跡を1秒後に描画します
canvas.GetMouseMove()
    .Select(e => e.EventArgs.GetPosition(canvas))
    .Delay(1000)
    .Subscribe(p =>Dispatcher.BeginInvoke(()=>
    {
        ellipse.SetValue(Canvas.LeftProperty, p.X - ellipse.Width / 2);
        ellipse.SetValue(Canvas.TopProperty, p.Y - ellipse.Height / 2);
    }));

退unfold: The Joy of Rx: Extension EventsDelay1DelayBeginInvoke


10使SubscribeReactive Framework