ReactiveProperty 5.2.0の新機能Awaitableを使ってみた

# 概要
ReactiveProeprty 5.2.0の新機能AwaitableなReactivePropertyを使ってみました。

ReactivePropertyの説明自体は下記を参照ください。
https://blog.okazuki.jp/entry/2015/12/05/221154

# AwaitableなReactivePropertyとは
ReactivePropertyがAwaitable パターンを実装したことで、`await`で値の変更を待機することができるようになりました。
今までも`Subscribe`で値の変更を購読することはできましたが、

- 1度だけ購読して何かしたい
- 値の変更を待機して、次の処理を続けて書きたい

といった時には少々手間でした。
Awaitableパターンが実装されたことにより、下記のような形で値の変更を待機することができます。

```csharp
var newValue = await MyReactiveProperty;
```

他にも`Subscribe`との相違点としては、ReactivePropertyを`Subscribe`すると`Mode`が`RaiseLatestValueOnSubscribe`ならばすぐに値が返ってきますが、
`await`した場合は、`Mode`に関係なく変更するまで待機します。

なお、Awaitableになったのは狭義のReactivePropertyだけでなく、
(ReadOnly)ReactiveProperty(Slim)と(Async)ReactiveCommand(Generic)もです。
つまりだいたい全部です。

ReactiveCommandの場合は`CanExcute`の変化ではなく、`Excute`呼び出しを待機して、返り値はExcute呼び出し時の引数です。

注意点として、今回のバージョンには破壊的変更が含まれています。
あまり実用的ではないのですが、5.1以前でもawaitで待機することは可能でした。
この場合はReactivePropertyが`Dispose`されるまで待機して、最後の値が返ってきます。
これはReactivePropertyというよりも、`IObsevable<T>`の機能です。

# デモ
デモプログラムを作ってみました。
WPFでファイルの変更を監視して、それをTextBoxに表示するアプリケーションを作ります。
ボタン押下後に監視を開始して、変更されたら1度だけTextBoxに反映させます。

## デモ実行結果

起動時
![スクリーンショット 2018-08-02 22.18.07.png](https://qiita-image-store.s3.amazonaws.com/0/120077/04fb1bf4-474b-f1fd-1472-90d2eb43de1a.png)

Button押下後、awaitで待機中はボタンが無効になっています。
![スクリーンショット 2018-08-02 22.15.47.png](https://qiita-image-store.s3.amazonaws.com/0/120077/00e9c228-510e-5316-d106-d8a2e091dfdb.png)

メモ帳で監視対象のファイルを変更します。
![スクリーンショット 2018-08-02 22.16.13.png](https://qiita-image-store.s3.amazonaws.com/0/120077/89db729a-6578-948f-6d97-75657c7c6661.png)

TextBoxに入力された文字が大文字化されて入ります。以降はファイルを変更しても反映されません。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/120077/c8d89fc0-6ba9-8da1-2e3e-5f7c046c1129.png)

## ViewModel

ViewModelでは`AsyncReactiveCommand`を用意して、コマンドの`Subscribe`内でmodelの`ReactivePropertySlim`の変更を`await`で待機しています。
待機している間、コマンドの`CanExcute`はfalseになります。
`ReactivePropertySlim`が変更されたら、その値を大文字化して、VM内の`ReactiveProperty`に反映させます。

```csharp:MainWindowViewModel.cs
class MainWindowViewModel : INotifyPropertyChanged
{
    public event PropertyChangedEventHandler PropertyChanged;

    private Model model = new Model();

    public ReactiveProperty<string> Name { get; } = new ReactiveProperty<string>("TARO");

    public AsyncReactiveCommand UpdateWaitLoadCommand { get; } = new AsyncReactiveCommand();

    public MainWindowViewModel()
    {
        UpdateWaitLoadCommand.Subscribe(DoSomeAsync);
    }

    private async Task DoSomeAsync()
    {
        model.StartLoad();
        string loadText = await model.LoadedText; //ReactivePropertyの変更を待つ

        Name.Value = loadText.ToUpper();
    }
}
```

## Model
Modelでは一定時間ごとにファイルを見て、Model内の`ReactivePropertySlim`に反映しています。

```csharp:Model.cs
class Model
{
    public ReactivePropertySlim<string> LoadedText { get; } = new ReactivePropertySlim<string>("");

    private const string inputPath = "inputText.txt";
    private Timer timerLoad = new Timer();

    public Model()
    {
        timerLoad.Elapsed += TimerLoad_Elapsed;
    }

    private void TimerLoad_Elapsed(object sender, ElapsedEventArgs e)
    {
        string loadedText = File.ReadAllText(inputPath);
        LoadedText.Value = loadedText;
    }

    /// <summary>
    /// ファイル監視の開始
    /// </summary>
    internal void StartLoad()
    {
        using (File.Create(inputPath)) { }

        timerLoad.Start();
    }
}
```

## View
ViewではVMの`ReactiveProperty`と`AsyncReactiveCommand`にBindingしています。

```xml:MainWindow.xaml
<Window
    x:Class="TestReactiveProperty52.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:TestReactiveProperty52"
    Width="300"
    Height="150">
    <Window.DataContext>
        <local:MainWindowViewModel />
    </Window.DataContext>
    <StackPanel>
        <TextBox Text="{Binding Name.Value, UpdateSourceTrigger=PropertyChanged}" />
        <Button Command="{Binding UpdateWaitLoadCommand}" Content="ファイルが変更されるのを待って反映" />
    </StackPanel>
</Window>
```


# 参考

https://runceel.github.io/ReactiveProperty/advanced/awaitable/#awaitable
https://ufcpp.net/study/csharp/sp5_awaitable.html

# 環境
VisualStudio2017
.NET Framework 4.7.1
C#7.1
ReactiveProperty 5.2.0