追記:IDisposableのAddToという拡張メソッドはReactivePropertyでのメソッドでしたorz
みたいな記事をりんちゃさんに拾われてた。
かずきさんの実装サンプルがあったので導入検討中 URL
今だとどうするかな~というと制約がなければReactive Extensionsかなぁ。例えば、こんなINotifyPropertyChangedの実装をしたクラスがあるとして
public class Person : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected virtual void OnPropertyChanged(string propertyName) { var h = this.PropertyChanged; if (h != null) { h(this, new PropertyChangedEventArgs(propertyName)); } } protected virtual bool SetProperty<T>(ref T field, T value, [CallerMemberName]string propertyName = null) { if (object.Equals(field, value)) { return false; } field = value; this.OnPropertyChanged(propertyName); return true; } private string name; public string Name { get { return this.name; } set { this.SetProperty(ref this.name, value); } } private int age; public int Age { get { return this.age; } set { this.SetProperty(ref this.age, value); } } }
Rx-MainをNuGetで追加してPropertyChangedイベントをIO
static class NotifyPropertyChangedExtensions { public static IObservable<PropertyChangedEventArgs> PropertyChangedAsObservable(this INotifyPropertyChanged self) { return Observable.FromEvent<PropertyChangedEventHandler, PropertyChangedEventArgs>( h => (s, e) => h(e), h => self.PropertyChanged += h, h => self.PropertyChanged -= h); } }
こうすると、Whereでプロパティ名を絞ってからSubscribeしてるので、そこで好きなようにやるかんじ。
var p = new Person(); var propertyChanged = p.PropertyChangedAsObservable(); var token = new CompositeDisposable(); token.Add(propertyChanged.Where(e => e.PropertyName == "Name") .Select(_ => p.Name) .Subscribe(name => Console.WriteLine(name))); token.Add(propertyChanged.Where(e => e.PropertyName == "Age") .Select(_ => p.Age) .Subscribe(age => Console.WriteLine(age))); // プロパティを変更したら値が出力される p.Name = "tanaka"; p.Age = 100; // 購読解除 token.Dispose(); // イベントの処理やめたので何もおきない p.Name = "kimura"; p.Age = 120;
もうちょっとプロパティの値の変更に特化して使いたいとかいうなら、ここまでラップしたメソッドを定義してもいいかも?
static class NotifyPropertyChangedExtensions { public static IObservable<PropertyChangedEventArgs> PropertyChangedAsObservable(this INotifyPropertyChanged self) { return Observable.FromEvent<PropertyChangedEventHandler, PropertyChangedEventArgs>( h => (s, e) => h(e), h => self.PropertyChanged += h, h => self.PropertyChanged -= h); } public static IObservable<T> PropertyChangedAsObservable<T, TProp>(this T self, Expression<Func<T, TProp>> propertySelector) where T : INotifyPropertyChanged { // プロパティ名を取り出して var name = ((MemberExpression)propertySelector.Body).Member.Name; return self.PropertyChangedAsObservable() // プロパティ名でフィルタリングして .Where(e => e.PropertyName == name) // 自分自身を返す感じ .Select(_ => self); } }
こうしとくと、こういう感じになる。
var p = new Person(); var token = new CompositeDisposable(); token.Add(p.PropertyChangedAsObservable(o => o.Name) .Subscribe(person => Console.WriteLine(person.Name))); token.Add(p.PropertyChangedAsObservable(o => o.Age) .Subscribe(person => Console.WriteLine(person.Age))); p.Name = "tanaka"; p.Age = 100; token.Dispose(); p.Name = "kimura"; p.Age = 120;
とりあえず
ReactivePropertyを入れておけば、拡張メソッドが定義されてたりします。PropertyChangdAsObservableメソッドの他にObservePropertyメソッドというのもあって以下のような感じで使えます。
ついでReactivePropertyには、IDisposableにAddToという拡張メソッドも定義されてるのでSubscribeした結果をCompositeDisposableに追加する部分のコードもすっきりします。のいえさん素敵。
var p = new Person(); var token = new CompositeDisposable(); p.ObserveProperty(o => o.Name).Subscribe(name => Console.WriteLine(name)).AddTo(token); p.ObserveProperty(o => o.Age).Subscribe(age => Console.WriteLine(age)).AddTo(token); p.Name = "tanaka"; p.Age = 100; token.Dispose(); p.Name = "kimura"; p.Age = 120;
素敵なかんじ。ということで、ObserveProperty使うのがよさげ。