BooleanNotifierをToReactiveCommandしたときの挙動、が思っていたのと違う理由


概要

この記事は以下の記事へのアンサーというか、私なりの推測をしたものです。

【WPF】BooleanNotifierをToReactiveCommandしたときの挙動について【ReactiveProperty7.5.1】

BooleanNotifierの挙動が思っていたのと違う?

同じ初期値falseを持つBooleanNotifierとReactivePropertyからReactiveCommandを生成するとCanExcuteの値が異なります。

var booleanNotifier= new BooleanNotifier(initialValue: false);
var reactiveProperty = new ReactiveProperty<bool>(initialValue: false);

BooleanNotifierButtonCommand = booleanNotifier.ToReactiveCommand();
ReactivePropertyButtonCommand = reactiveProperty.ToReactiveCommand();
//BooleanNotifierButtonCommand.CanExcute == true;
//ReactivePropertyButtonCommand.CanExcute == false;

コード的原因

コード的には元記事でも述べられているように、以下のところがまさに原因です。

(BooleanNotifierにはなく、ReactivePropertyでのみ)Subscribeの中でOnNext()が記述されている

でもなんで?(本題)

ここからが記事の本題ですが、では何故BooleanNotifierはそうしないのか?という疑問があるかもしれません。

それは実はSubscribeされたらOnNextをするReactivePropertyのほうが、Rx的にもしくはObserverパターンとしては異質なことをしているから、です。

Rx入門 (2) - オブザーバーパターン

Observerパターンの説明では通常、Observerが購読(Subscribe)した直後に値が流れて来ることはなく、Observableが通知(OnNext)して初めて値がObserverに来ます。

しかし、ReactivePropertyでは異なる動きをします。これはMVVMパターンの中で使われる上でそのほうが便利だからです。それは前述のToReactiveCommand()でもそうですし、あるReactivePropertyから別のReadOnlyReactivePropertyに変換する場合もそうです。この動きはReactivePropertyのModeRaiseLatestValueOnSubscribeを無効にすれば無くすこともできます。

ただ、この判断には躊躇いもあったようで、途中で破壊的変更をして導入されています。

ReactivePropertyのデフォルトモードが DistinctUntilChanged|RaiseLatestValueOnSubscribe になりました。今まではRaise…が入ってなかったのですが、思うところあって変わりました。例えばCombineLatestは、全てが一度は発火していないと動き出しません。ReactiveCommandの条件に使うなどの場合にRaiseしてくれないと不都合極まりなく、かつ、Subscribeと同時にRaiseすることによる不都合なシーンは逆に少ない。ことを考えると、必然的にデフォルトをどちらに振るべきかは、分かりきった話でした。

というわけで、BooleanNotifierがReactivePropertyと違う挙動をするのは、ReactivePropertyがMVVMパターンを意識して本来のObserverパターンとは違う挙動をするようなクラスだから、です。

じゃあ、BooleanNotifierはどう使うか、というと本来のObserverパターンらしく何かの変更通知です。ロギングやバックアップ処理など、Viewに現在の状態を常に示す、という以外の用途ですね。

余談

実は同じNotifier系でもBusyNotifierはSubscribeされたらOnNextします。これはToReactiveCommand()などで使われることが想定されたクラスだからです。
https://blog.okazuki.jp/entry/2016/04/09/005825