はじめに
@okazuki さん がメンテナーを務めておられる ReactiveProperty に関する記事になります。
趣味のWPFアプリ開発で長い間 愛用させていただいているのですが、勘違い(勝手な思い込み)をしているところが見つかり、かずき さんに質問した内容をまとめた記事になります。
知ってる人にはとっては「今更何言っての?」って内容かもしれません 😭
本題
ReactiveCommand を作成する場合、 WithSubscrbe() を使って Action の購読までメソッドチェーンで一気に書くことがあります。
以下のコードでは ICommand.CanExecute() を 1秒ごとに切り替えます。
TimerCommand = Observable.Timer(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1))
.Select(x => (x & 1) == 0)
.ToReactiveCommand()
.WithSubscribe(() => Message = $"Clicked!", _disposables.Add);
WithSubscribe() に Disposer を指定しており、_disposables.Dispose() を実行すれば 後腐れなく全てが破棄 される。 と思い込んでおりましたが、その認識は間違いでした。
実際に Disposer に登録されるのは 購読した Action のみで、 ReactiveCommand 自体は登録されません。
そのため上のコードでは、_disposables.Dispose() 後も Timer が動作し続けて、ReactiveCommand.CanExecute() は変化し続けます。(Action は破棄済みなので Command を実行しても何も起きません)
対策
ReactiveCommand.WithSubscribe() の使用時は ReactiveCommand 自体も破棄の対象に登録しましょう。
TimerCommand = Observable.Timer(TimeSpan.FromSeconds(0), TimeSpan.FromSeconds(1))
.Select(x => (x & 1) == 0)
.ToReactiveCommand()
.WithSubscribe(() => Message = $"Clicked!", _disposables.Add)
.AddTo(_disposables); // ◆これが必要!!
思い込みって怖いですね。
サンプルコード
hsytkm/RpTimerDemo: Dispose ReactiveCommand when using WithSubscribe method.
環境
VisualStudio 2022 17.2.0 Preview 1.0
.NET 6.0 + C# 10.0 + WPF
ReactiveProperty 8.0.3
↧