結局ReactivePropertyはどれを使えばいいのか、まとめてみた

私は最近、VBのWinFormsアプリでスパゲティをほぐしたりおかわりさせたりする作業からC#でのWPFアプリケーションのシステム開発と業務内容が大きく変わりました。当然MVVMなのでRxを活用しているのですが、ちょっと調べた程度ではいまいち`ReactiveProperty`たちの違いがピンと来なかったので、備忘録と将来の初学者の方たちのために本稿にまとめます。

# 2024/08/23 編集
だいぶ遅れましたが、v 9.0で追加された内容について追記しました。

# 2023/02/28 追記
02/12にv 9.0がリリースされました。
更新事項は@okazukiさんのZennnの記事を御覧ください。

https://zenn.dev/microsoft/articles/reactiveproperty-v9-0-0

私はまだアップデートできておりませんが、記事を読んで重要かな～と思ったのは以下の点です。

* コマンドのスリム版`ReactiveCommandSlim`の追加
* バリデーション機能を持つ`ValidatableReactiveProperty<T>`の追加
* Notifier 系クラスの `BooleanNotifier, BusyNotifier, CountNotifier, MessageBroker, AsyncMessageBroker`がスレッドセーフではなくなった（従来と同じくスレッドセーフ版の`**Legacy`クラスもある

# 前提知識

* オブザーバーパターンをある程度理解していること
    * こちらの記事がとてもわかりやすいです

https://qiita.com/yutorisan/items/6e960426da71b7e02af7

* `ReactiveProperty<T>`を使ってMVVMアプリケーションの実装を少しでもしたことがあること
    * 全く経験のない方は初学者ですらないため本稿の対象外です、まずはHelloWorldアプリでも作ってみてください

# Reactiveたち
`Reactive`と名のつくクラスは以下の一覧になります。

|クラス|簡単な説明|
|-|-|
|`ReactiveProperty<T>`|基本となるクラス。`Value`プロパティで値の書き換えが可能|
|`ReadOnlyReactiveProperty<T>`|`Value`プロパティで値の取得のみ行える版。データはソースとなる`IObservable<T>`から流れてくる|
|`ReactivePropertySlim<T>`|`ReactiveProperty<T>`の軽量版。**理由がなければこれを使う**|
|`ValidatableReactiveProperty<T>`|入力された`T`に対するバリデーション機能を持つ軽量なクラス。|
|`ReadOnlyReactivePropertySlim<T>`|`ReadOnlyReactiveProperty<T>`の軽量版。理由がなければこれを使う|
|`ReactiveCollection<T>`|コレクションの追加・変更・削除操作を行う場合に使うクラス。|
|`ReadOnlyReactiveCollection<T>`|コレクションの内容がソースとなる`IObservable<T>`または各操作の`CollectionChanged<T>`によって決まるクラス|
|`ReactiveCommand`|ボタン等のコマンドのクラス。`Subscribe`メソッドには値が流れてこないため`Action`を設定する|
|`ReactiveCommand<T>`|イベント引数を使いたいときなどに使う。`Subscribe`メソッドには`T`が流れてくるため`Action<T>`を設定する|
|`ReactiveCommandSlim`|`ReactiveCommand`の軽量版。**理由がなければこれを使う**|
|`ReactiveCommandSlim<T>`|`ReactiveCommand<T>`の軽量版。**理由がなければこれを使う**|
|`AsyncReactiveCommand`|コマンドに設定した非同期メソッドが完了するまで`Execute`できなくなるコマンド。`Subscribe`メソッドには`Func<Task>`(または`async void`)を設定する|
|`AsyncReactiveCommand<T>`|上記の引数が流れてくる版。`Func<T, Task>`を設定する|
|`ReactiveTimer`|タイマーの`Reacitve`版。スタート、ストップができる。タイマー到達時に`Subscribe`に流れてくるのはタイマー到達回数|

これ以外の機能はこちらの記事によくまとまっているので参照してください。

https://qiita.com/kwhrkzk/items/ed0f74bb2493cf1ce60f
# `ReactiveProperty<T>`と`ReadOnlyReactiveProperty<T>`の使い分け
２つの違いは単純に「値を`Value`プロパティ経由で直接変えるかどうか」です。以下２パターンのどちらかをするなら`ReadOnly`でない方を使います。

* ViewModelのコードから`Rp.Value = 1;`のように値を代入する場合
* xamlで`{Binding Rp.Value, Mode=TwoWay}`のように画面からVMのプロパティへ値が設定される方向にバインドする場合（`TextBox`など）

`IObservable<T>`から流れてきた値や、他の`ReactiveProperty<T>`の内容を元に値が一意に決まるような場合は`ReadOnlyReactiveProperty<T>`を使いましょう。これは`ReactivePropertySlim<T>`と`ReadOnlyReactivePropertySlim<T>`の場合も同じです。

# `ReactiveProperty<T>`と`ReactivePropertySlim<T>`の使い分け
~~すごく雑にまとめると`TextBox`の`Text`プロパティを`BindingMode=TwoWay`で受ける場合は`Slim`でない`ReactiveProperty<T>`を使うということが言えます。~~
**v 9.0で事情が変わりました。基本的に`ReactivePropertySlim<T>`を使用します。ただし、入力検証が必要な場合は`ValidatableReactiveProperty<T>`を使用することになります。** あえて`Slim`でないほうを使うのは、下記にあるように「別スレッドで発生した`PropertyChanged`イベントを必ずUIスレッドで処理したいケース」のみとなります。

以下、`ReactivePropertySlim<T>`にできないことを記述します。以下のことをやりたい場合でなければ、**`Slim`のほうを基本的に使いましょう**。以下のドキュメントの紹介では、インスタンスを作る時間は5倍、基本的なユースケースでは36倍の速度差があるそうです。本節では一部の説明及びコードを以下から引用します。

https://okazuki.jp/ReactiveProperty/features/ReactivePropertySlim.html

#### UIスレッドへのディスパッチは自動で行われない
`Observable.Timer`などのように**非同期に動作する`IObservable<T>`を作って画面にバインドしたいとき**、`Slim`版ではUIスレッドへのディスパッチ、つまりスレッド切り替えが行われずに値が流れます。そうなると、画面要素の別スレッドからのアクセスになるため`InvalidOperationException`が発生してしまいます。
対策としては通常の`(ReadOnly)ReactiveProperty<T>`を使うほか、明示的にディスパッチしてやればいいようです。つまり、以下の実装となります。

```C#
var rp = Observable.Interval(TimeSpan.FromSeconds(1))
    .ObserveOnUIDispatcher() // dispatch to UI thread
    .ToReadOnlyReactivePropertySlim();
```

当然ですが、非同期で動作するわけではない`IObservable<T>`から作った`(ReadOnly)ReactivePropertySlim<T>`なら全く問題なく画面にバインドできます。
#### 書き換え可能な`ReactivePropertySlim<T>`は`IObservable<T>`から作れない
`IObservable<T>`からは、`ReadOnlyReactivePropertySlim<T>`しか作れません。理由は以下となります[^1]。
>機能的に低下した所は他に、ToReactivePropertySlimがありません。これは、Sourceから流れてくるのとValueへのセットの二通りで値が変化する(Mergeされてる)のが気持ち悪いというか、使いみちあるのそれ？みたいに思ったからです。ない、とはいわないまでも、存在がおかしい。のでいっそ消しました。かわりにToReadOnlyReactivePropertySlimがあります。値の変化はSourceからのみ。このほうが自然でしょふ。

よって、`IObservable<T>`から作りつつ`Value`から値をセットしたい（`BindingMode`が`TwoWay`や`OneWayToSource`も含む）場合は素直に`ReactiveProperty<T>`を使いましょう。
もちろん、`IObservable<T>`から作らずに普通に`new`する場合、`BindingMode=TwoWay`でも`ReactivePropertySlim<T>`を`Textbox`の`Text`にバインドすることはできます。しかし後述の理由によりほとんど多くのケースでは`ValidatableReactiveProperty<string>`（v 9.0未満の場合は`ReactiveProperty<string>`）を使うことになります。

#### DataAnnotations属性を使ったバリデーションはできない
例えば`TextBox`の入力内容でバリデーションをかけたい場合、`System.ComponentModel.DataAnnotations`名前空間にある属性を使って`ReactiveProperty<string>`で以下の実装をすることができます。

```c#
using System.ComponentModel.DataAnnotations;
public class ViewModel
{
    // 必須かつ 0以上100以下の数値のみ受付
    [Required("入力が必須です")]
    [Range(0, 100, "入力値が範囲外です")]
    public ReactiveProperty<string> InputedText { get; }
}
```
これは`ReactivePropertySlim<T>`には削ぎ落とされた機能になるため使用できません。画面入力が完全に任意入力可能なことってあまりないと思いますので、バリデーションを実現するために`ReactivePropertySlim<T>`は使えないことになります。

v 9.0より古いバージョンの場合、`TextBox`などの入力項目にバインドするものは`DataAnnotations`を使うために`ReactiveProperty<T>`を使うことになるでしょう。

v 9.0以降の場合、`DataAnnotations`を使わずに`ValidatableReactiveProperty<T>`を使用することでバリデーションの実装が可能です。

# `ReactiveCommand`と`ReactiveCommand<T>`の使い分け
表に書いた通り、コマンドに引数があるかどうかで使い分けます。
**※ v 9.0以降の場合、基本的にはより軽量で高速な`ReactiveCommandSlim`または`ReactiveCommandSlim<T>`を使用します。**

##### 引数なしコマンド
以下のケースにおける`OnClickedCommand`と`LoadedCommand`は`ReactiveCommand`です。

```xml:view.xaml
<UserControl x:Class="Sample.View"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
    <!-- 以下略 -->
    >
    <i:Interaction.Triggers>
        <i:EventTrigger EventName="Loaded">
            <i:InvokeCommandAction Command="{Binding LoadedCommand}" />
        </i:EventTrigger>
    </i:Interaction.Triggers>
    <Button Content="Button"
        Command="{Binding OnClickedCommand}" />
</UserControl>
```

```C#:ViewModel.cs
using System.Diagnostics;
public class ViewModel
{
    public ReactiveCommand LoadedCommand { get; }
    public ReactiveCommand OnClickedCommand { get; }
    public ViewModel()
    {
        // Subscribeの中身はActionなので() => { }で記述する
        LoadedCommand = new ReactiveCommand().
            WithSubscribe(() => Debug.WriteLine("Loaded"));
        // 引数なしなのでこんなふうにも略記できる
        OnClickedCommand = new ReactiveCommand().WithSubscribe(Write);

        void Write() => Debug.WriteLine("OnClicked");
    }
}
```

##### 引数ありコマンド
以下のケースにおける`OnClickedCommand`は`CommandParameter`で`Boolean`を指定しているため`ReactiveCommand<bool>`です。以下の例の他にも、`EventToReactiveCommand`を使う場合などで`ReactiveCommand<T>`が登場するでしょう。

```xml:view.xaml
<UserControl x:Class="Sample.View"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:system="clr-namespace:System;assembly=mscorlib"
    <!-- 以下略 -->
    >
    <Button Content="Ok"
        Command="{Binding OnClickedCommand}" >
        <Button.CommandParameter>
            <system:Boolean>True</system:Boolean>
        </Button.CommandParameter>
    </Button>
    <Button Content="Cancel"
        Command="{Binding OnClickedCommand}" >
        <Button.CommandParameter>
            <system:Boolean>False</system:Boolean>
        </Button.CommandParameter>
    </Button>
</UserControl>
```

```C#:ViewModel.cs
using System.Diagnostics;
public class ViewModel
{
    public ReactiveCommand<bool> OnClickedCommand { get; }
    public ViewModel()
    {
        // Subscribeの中身はAction<bool>
        OnClickedCommand = new ReactiveCommand().
            WithSubscribe(isOkClicked => 
            {
                if (isOkClicked) // OKボタンクリック時処理
                else // Cancelボタンクリック時処理
            });
    }
}
```

# `ReactiveCommand`と`ReactiveCommandSlim`の使い分け
`ReactiveProperty`のケースと同様に、`Slim`版は`CanExecuteChanged`イベントの購読がUIスレッドにディスパッチされなくなっています。そのぶんインスタンスを作る時間は70倍、基本的なユースケースでは13倍の速度差があるそうです。コマンドはボタンなどの画面部品にバインドすることが多いので、UIスレッドを2個以上増やして並列に相互作用したい、といったケース[^2]でもなければ全部`Slim`でよいと思います。

https://zenn.dev/microsoft/articles/reactiveproperty-v9-0-0#reactivecommandslim

なお、`ReactiveCommandSlim`は`AsyncReactiveCommand`のように、**実行可能状態をほかの`Command`と共有できるようになりました**。この点からも`Slim`でないほうを使うケースはほとんどなくなるでしょう。以下のサンプルコードは上記の記事からの引用です。

```c#
// CanExecute のソースになる IObservable<bool>
IObservable<bool> someCanExecuteSource = ...;
// これを共有することで CanExecute の状態を共有可能にする
ReactivePropertySlim<bool> sharedStatus = new();

// command1 と command2 で CanExecute の状態が同期される
// AsyncReactiveCommand はコマンド実行中に自動的に CanExecute が false になるので command1 が実行中は sharedStatus 経由で command2 の CanExecute も false になる
AsyncReactiveCommand command1 = someCanExecuteSource.ToAsyncReactiveCommand(sharedStatus)
    .WithSubscribe(async () =>
    {
        await Task.Delay(3000); // 何か時間のかかる処理
    });
ReactiveCommandSlim command2 = someCanExecuteSource.ToReactiveCommandSlim(sharedStatus)
    .WithSubscribe(() =>
    {
        // do something
    });
```

# `ReactiveCommand`と`AsyncReactiveCommand`の使い分け
`ReactiveCommand`は`IObservable<bool>`から生成することもできます。最後の値が`CanExecute()`の戻り値になるため、例えば`Button`なら`IsEnabled`を管理しなくても以下のように作った`ReactiveCommand`をバインドすることで簡単に押下可否を制御できるようになっています。

```c#:ViewModel.cs
public class ViewModel
{
    public ReactiveCommand OnClickedCommand { get; }
    public ViewModel()
    {
        // 5秒おきに押せる/押せないが切り替わるボタン
        OnClickedCommand = Observable.Interval(TimeSpan.FromSeconds(5)).
            Select(i => i % 2 == 0).
            ToRectiveCommand().
            WithSubscribe(() => 
            {
                System.Diagnotics.Debug.WriteLine("Clicked!");
            });
    }
}
```

ただし、上記方法では**ボタンを連打したら連打しただけ"Clicked!"が出力されてしまいます。**これは、「ボタンの押下時処理(`Subscribe`に指定した`Action`)が完了するまでボタンを押下不可にする」という制御がかかっていないためです。
これを本来のボタン押下可否の`IObservable<bool>`と一緒に制御するのは、`CombineLatestValuesAreAllTrue`など使えば実装可能ですが煩雑ですし頻出パターンです。そこで、**`Subscribe`に指定した`async`メソッドが完了するまで`CanExecute()`が`false`になるコマンド**として`AsyncReactiveCommand`を利用できます。

```c#:ViewModel.cs
public class ViewModel
{
    public AsyncReactiveCommand OnClickedCommand { get; }
    public ViewModel()
    {
        // 5秒おきに押せる/押せないが切り替わり、二重押しもできないボタン
        OnClickedCommand = Observable.Interval(TimeSpan.FromSeconds(5)).
            Select(i => i % 2 == 0).
            ToAsyncRectiveCommand().
            WithSubscribe(async () => // 非同期メソッドにする
            {
                await Task.Delay(1000); // なんか重い処理
                System.Diagnotics.Debug.WriteLine("Clicked!");
            });
            // Taskが返ってくればいいので非同期処理しないならこれでもOK
            WithSubscribe(() =>
            {
                System.Diagnotics.Debug.WriteLine("Clicked!");
                return Task.CompletedTask;
            });

    }
}
```
上記実装をすると、`OnClickedCommand`はボタン押し後に"Clicked"が表示されるまでの約1秒間、つまり非同期メソッドが完了するまで押せない状態になります。

ただし、この「完了するまで再度ボタンが押せない状態」を他のボタンの`AsyncReactiveCommand`と共有することはできません。これをしたい場合は、`Reactive.Bindings.Notifiers`名前空間の`BusyNotifier`を使って以下のように実装できます。ただしこのパターンは`Async`でない`ReactiveCommand`にする必要があります。

```c#:ViewModel.cs
using Reactive.Bindings.Notifiers;
public class ViewModel
{
    public BusyNotifier BusyNotifier { get; } = new();
    public ReactiveCommand OnClickedCommand1 { get; }
    public ReactiveCommand OnClickedCommand2 { get; }
    public ReactiveCommand OnClickedCommand3 { get; }
    public ViewModel()
    {
        var isEnabled = Observable.Interval(TimeSpan.FromSeconds(5)).
            Select(i => i % 2 == 0).
            // BusyNotifierはIObservable<bool>
            // ただしBusyなときにtrueなので反転する
            CombineLatest(BusyNotifier, (x, y) => x && !y);

        // 5秒おきに押せる/押せないが切り替わるボタン
        // 二重押しと他のボタンが押してる間も押せない
        OnClickedCommand1 = isEnabled.
            ToRectiveCommand().
            WithSubscribe(async () => await OnClicked("1"));
        OnClickedCommand2 = isEnabled.
            ToRectiveCommand().
            WithSubscribe(async () => await OnClicked("2"));
        OnClickedCommand3 = isEnabled.
            ToRectiveCommand().
            WithSubscribe(async () => await OnClicked("3"));

        // 例のため1秒間押せなくしたいのでasyncメソッドにしているが、
        // 同期処理でいいなら普通のvoidにすればOLK
        async Task OnClicked(string no)
        {
            if (BusyNotifier.IsBusy) return;
            using (BusyNotifier.ProsessStart())
            {
                await Task.Delay(1000);
                System.Diagnotics.Debug.WriteLine($"Button{no} Clicked!");
            }
        }
    }
}
```

`ReactiveCommad<T>`と`AsyncReactiveCommand<T>`の使い分けは全く同じなので省略します。

# `ReacitveCollection<T>`と`ReadOnlyReacitveCollection<T>`の使い分け
`ReactiveProperty<T>`と`ReadOnlyReactiveProperty<T>`のケースと同様に、`ObservableCollection<T>`から流れてきた値や、他の`ReactiveCollection<T>`の内容を元に値が一意に決まるような場合は`ReadOnlyReacitveCollection<T>`を使いましょう。

サーバーから取得したデータ群に、画面操作で新しくデータを増やしたいときは`ReactiveCollection<T>`が向いています。例えば「行追加ボタン」のあるケースです。
２つの共通点として、ソースとなった`IObservable<T>`から値が流れてきた場合、すべて`Add`されます。そのため必要なら適切なタイミングで`Clear`する必要があることに注意してください。

# `ReadOnlyReacitveCollection<T>`と`ReadOnlyReacitveProperty<IEnumerable<T>>`の使い分け
こちらは特殊パターンです。以下の２つは直感的には同じに見えませんか？

* コレクションの各要素をコードから操作したりしない。コレクションの中身はすべてソースとなる`IObservable<T>`から決まる
* コレクションの操作をコードからはしない。内容が更新される場合はすべて`Clear`して`AddRange`する（洗い替え）

データを一覧表示する画面を考えてみてください。一覧画面ではサーバーから受け取った複数件のデータを画面表示しますが、画面からデータを追加したり各行の内容を更新できる必要がない場合があります。このような場合にはコレクションへの`Add`、`Reset`、`Update`、`Delete`の各操作と監視が不要です（というかデータ取得のたびに１件ずつ`Add`のイベントが起きられても困る）。
そのため、**Model側では`IEnumerable<T>`でデータを持っておき、それを`ReadOnlyReactiveProperty<IEnumerable<T>>`にする方法が考えられます**。

```C#
public class Model : INotifyPropertyChanged
{
    public IEnumerable<MyData> Received { get; private set => /* 変更通知処理 */ }
    public void Request()
    {
        // サーバへのデータ問い合わせ
    }
}
public class ViewModel
{
    // 取得したデータ(型名が長い...)
    public ReadOnlyReactivePropertySlim<IEnumerable<MyData>> Items { get; }
    public ViewModel(Model model)
    {
        // IObservable<IEnumerable<T>>を生成してModelのデータを監視
        Items = model.ObserveProperty(m => m.Received).
            // もし必要ならSelectメソッドを噛ましてデータを変換する
            // C#9.0以降でNullable=enableなら型指定が必要
            // (ないとTが<IEnumerable<MyData>>?に推論される)
            ToReadOnlyReactivePropertySlim<IEnumerable<MyData>>();
    }
}
```
`ReadOnlyReactiveProperty<IEnumerable<T>>`を使うことによる利点と欠点を述べます。

* 利点
    * Modelがデータを受信したとき、`Received`の値変更の通知が1回だけになる
        * `ObservableCollection<T>`から`ReadOnlyReactiveCollection<T>`にする場合、洗い替えして10件のデータになると`Clear`の1回と`Add`の10回で変更通知が11回起きるため、場合によっては速度改善になりえる
    * 要素を追加・削除しないことが明確になる
* 欠点
    * １要素だけを追加・削除したりすることができない
    * 内容の更新は監視できない。`ListView`等の`ItemSource`に指定しているとき、特定の要素の内容を変えたりしても変更は通知されない
        * 例えば`IsSelected`を表示用データにバインドさせたい場合、`Items`から要素のどれかの`IsSelected`が変更されたかの監視をViewModelからはできない[^3]。これは`ObserveElementProperty`が`(ReadOnly)ObservableCollection<T>`の拡張メソッドのため。

個人的には以下の条件をすべて満たす場合、`ReactiveCollection<T>`ではなく`ReactiveProperty<IEnumerable<T>>`にしています。

1. コレクションの各要素の監視が不要
1. コレクションが更新されるときは常に洗い替えで全件更新

#### `IEnumerable<T>`から`ReadOnlyReactiveCollection<T>`の生成
「Modelは常に洗い替えでデータを取得するが、各要素の状態を監視する必要があるためViewModelでは`ReactiveCollection<T>`で保持したい」というケースがあります。この場合は`SelectMany`を使うことで、`IObservable<IEnumerable<T>>`から`ReadOnlyReactiveCollection<T>`を作ることが可能です。
ただし、Modelの`IEnumerable<T>`プロパティが洗い替えされるときは一度`ReadOnlyReactiveCollection<T>`をクリアする必要があります。クリアしないと前節の通り**全件`Add`** されるからです。`ToReadOnlyReactiveCollection<T>()`の第一引数に`IObservable<Unit>`を渡すと`OnNext`されたときにクリアされるコレクションを作れるので、検索ボタンなどプル型でModelのデータが更新される場合は更新トリガーの前にクリアするようにしましょう。

```c#
public class Model : INotifyPropertyChanged
{
    public IEnumerable<MyData> Received { get; private set => /* 変更通知処理 */ }
    public void Request()
    {
        // サーバへのデータ問い合わせ
    }
}

public class ViewModel
{
    private Model _model;
    // コレクションをクリアするためのオブザーバー
    private Subject<Unit> _clearSubject = new();
    public ReadOnlyReactiveCollection<MyData> Items { get; }
    public ReadOnlyReactivePropertySlim<int> SelectedCount { get; }
    public ViewModel(Model model)
    {
        _model = model;

        // IObservable<IEnumerable<MyData>>を生成してModelのデータを監視
        Items = model.ObserveProperty(m => m.Received).
            // SelectManyでIObservable<T>に変換
            SelectMany(mydata => mydata /* 必要なら別のTに変換する処理を書く */).
            ToReadOnlyReactiveCollection(_clearSubject);

        // データのIsSelectedを監視して変わったらその度にカウント
        SelectedCount = Items.
            ObserveElementProperty(item => item.IsSelected).
            Select(_ => Items.Count(item => item.IsSelected)).
            ToReadOnlyReactivePropertySlim();
    }

    /// <summary>検索ボタン押下時処理</summary>
    private void OnSearchButtonClicked()
    {
        // 現在のデータをクリアしてから取得しにいく
        _clearSubject.OnNext(Unit.Default);
        _model.Request();
    }
}
```

# あとがき
VBでWinFormsをいじっている間は「リアクティブプログラミング？なにそれおいしいの？」という気持ちでいましたが、オブザーバーパターンを理解すると「これはすごいぞ！」と理解できるようになりました。今では`Rx`なしでコーディングすることはできません。
ただ、まだまだ使いこなせてない機能が多いです。本稿は記事作成時点での理解で書いているため間違っている部分があればどしどし指摘していただけると幸いです。

なお、本稿で一番重要なのは以下だと思っています。

* <font color="Red">**理由がなければ`ReactivePropertySlim<T>`を使う**</font>



[^1]: http://neue.cc/2018/01/18_562.html
[^2]: UIスレッドを増やすのはそもそもアンチパターン
[^3]: `T`の各要素をすべて個別に監視するなら可能だが、そんなことをするくらいなら`ReactiveCollection<T>`で`ObserveElementProperty`するほうが自然