泥庭

2015年3月10日

【ReactiveProperty】きちんとDisposeしよう

Filed under: .NET, C#, WPF — タグ: , , , — yone64 @ 3:05 PM
ReactivePropertyが依存しているReactiveExtensionsのお話なのですが、きちんとにDisposeしないと予期しない動作の原因になります。

下記サンプル
MainWindow.xaml
<Window x:Class="WpfApplication3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Viewbox>
            <TextBlock Text="{Binding DateTime.Value}" />
        </Viewbox>
    </Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication3
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        private IDisposable _disposable;

        public MainWindow()
        {
            InitializeComponent();

            var vm = new MainViewModel();
            this.DataContext = vm;

            this._disposable = vm as IDisposable;
            this.Closing += (s, e) =>
            {
                if (this._disposable != null) this._disposable.Dispose();
            };
        }
    }
}
MainViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
using Reactive.Bindings;
 
namespace WpfApplication3
{
    public class MainViewModel : IDisposable
    {
        public ReactiveProperty<string> DateTime { get; private set; }
 
        public MainViewModel()
        {
            this.DateTime = 
                Observable.Interval(TimeSpan.FromMilliseconds(10), NewThreadScheduler.Default)
                          .Select(_ => System.DateTime.Now.ToString("HH:mm:ss.fff")).ToReactiveProperty();
        }
 
        void IDisposable.Dispose()
        {
            this.DateTime.Dispose();
        }
    }
}
タイマーで時間を表示するだけの簡単なサンプルですが、ReactivePropertyをDisposeしないと終了しないアプリケーションになってしまいます。
この場合は、NewThreadSchedulerによって新しいThreadが立っているので当然の結果なのですが、Rxを使うと簡単にThreadを起こせるので、神経質にDisposeしていたほうが良い気がしました。

というわけで、基底クラスとして次のようなものを用意するようにしています。
ViewModelBase.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Disposables;
using System.Text;
using System.Threading.Tasks;

namespace WpfApplication3
{
    public abstract class ViewModelBase : IDisposable
    {
        protected CompositeDisposable Disposable { get; private set; }

        public ViewModelBase()
        {
            this.Disposable = new CompositeDisposable();
        }

        void IDisposable.Dispose()
        {
            this.Disposable.Dispose();
        }
    }
}
CompositeDisposableは、Rxが提供しているIDisposableをまとめるためのクラスです。
これを利用すると、MainViewModelが次のように少し簡易になります。
MainViewModel.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reactive.Concurrency;
using System.Reactive.Linq;
using System.Text;
using System.Threading.Tasks;
using Reactive.Bindings;
 
namespace WpfApplication3
{
    public class MainViewModel : ViewModelBase
    {
        public ReactiveProperty<string> DateTime { get; private set; }
 
        public MainViewModel()
        {
            this.DateTime = 
                Observable.Interval(TimeSpan.FromMilliseconds(10), NewThreadScheduler.Default)
                          .Select(_ => System.DateTime.Now.ToString("HH:mm:ss.fff"))
                          .ToReactiveProperty()
                          .AddTo(this.Disposable); // かずきさん指摘により修正

            // this.Disposable.Add(this.DateTime);
        }
    }
}
ToReactiveProperty()とDisposable.Add()をまとめてうまく書けるといいんですけどね。↓のような、拡張メソッドを増やすのしか思いつきませんでした。
public static class ViewModelBaseEx
{
    public static ReactiveProperty<T> ToReactiveProperty<T>(this IObservable<T> souce, CompositeDisposable disposable)
    {
        var rp = souce.ToReactiveProperty();
        disposable.Add(rp);
        return rp;
    }
}
広告

1件のコメント »

  1. ReactivePropertyを作ってCompositeDisposableに追加するのはExtensions名前空間にあるAddTo拡張メソッドでいけると思います。

    this.DateTime = hoge……ToReactiveProperty().AddTo(this.Disposable);

    みたいな感じで。

    コメント by かずき — 2015年3月10日 @ 3:55 PM


RSS feed for comments on this post. TrackBack URI

コメントを残す

以下に詳細を記入するか、アイコンをクリックしてログインしてください。

WordPress.com ロゴ

WordPress.com アカウントを使ってコメントしています。 ログアウト / 変更 )

Twitter 画像

Twitter アカウントを使ってコメントしています。 ログアウト / 変更 )

Facebook の写真

Facebook アカウントを使ってコメントしています。 ログアウト / 変更 )

Google+ フォト

Google+ アカウントを使ってコメントしています。 ログアウト / 変更 )

%s と連携中

WordPress.com Blog.

%d人のブロガーが「いいね」をつけました。