みたいな記事をりんちゃさんに拾われてた。
かずきさんの実装サンプルがあったので導入検討中 URL
今だとどうするかな~というと制約がなければReactive Extensionsかなぁ。例えば、こんなINotifyPropertyChangedの実装をしたクラスがあるとして
publicclass Person : INotifyPropertyChanged { publicevent PropertyChangedEventHandler PropertyChanged; protectedvirtualvoid OnPropertyChanged(string propertyName) { var h = this.PropertyChanged; if (h != null) { h(this, new PropertyChangedEventArgs(propertyName)); } } protectedvirtualbool SetProperty<T>(ref T field, T value, [CallerMemberName]string propertyName = null) { if (object.Equals(field, value)) { returnfalse; } field = value; this.OnPropertyChanged(propertyName); returntrue; } privatestring name; publicstring Name { get { returnthis.name; } set { this.SetProperty(refthis.name, value); } } privateint age; publicint Age { get { returnthis.age; } set { this.SetProperty(refthis.age, value); } } }
Rx-MainをNuGetで追加してPropertyChangedイベントをIO
staticclass NotifyPropertyChangedExtensions { publicstatic 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(); propertyChanged.Where(e => e.PropertyName == "Name") .Select(_ => p.Name) .Subscribe(name => Console.WriteLine(name)).AddTo(token); propertyChanged.Where(e => e.PropertyName == "Age") .Select(_ => p.Age) .Subscribe(age => Console.WriteLine(age)).AddTo(token); // プロパティを変更したら値が出力される p.Name = "tanaka"; p.Age = 100; // 購読解除 token.Dispose(); // イベントの処理やめたので何もおきない p.Name = "kimura"; p.Age = 120;
もうちょっとプロパティの値の変更に特化して使いたいとかいうなら、ここまでラップしたメソッドを定義してもいいかも?
staticclass NotifyPropertyChangedExtensions { publicstatic IObservable<PropertyChangedEventArgs> PropertyChangedAsObservable(this INotifyPropertyChanged self) { return Observable.FromEvent<PropertyChangedEventHandler, PropertyChangedEventArgs>( h => (s, e) => h(e), h => self.PropertyChanged += h, h => self.PropertyChanged -= h); } publicstatic 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(); p.PropertyChangedAsObservable(o => o.Name) .Subscribe(person => Console.WriteLine(person.Name)).AddTo(token); p.PropertyChangedAsObservable(o => o.Age) .Subscribe(person => Console.WriteLine(person.Age)).AddTo(token); p.Name = "tanaka"; p.Age = 100; token.Dispose(); p.Name = "kimura"; p.Age = 120;
とりあえず
ReactivePropertyを入れておけば、拡張メソッドが定義されてたりします。PropertyChangdAsObservableメソッドの他にObservePropertyメソッドというのもあって以下のような感じで使えます。
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使うのがよさげ。