Rx を習得するために、ファイルの読み取りを模倣し、キャンセルをサポートする単純なアプリケーションを作成しています。
それはこのように動作します:
ユーザーが「実行」ボタンを押すと、プログラム
- ビジー インジケータを表示し、
- ファイルをチャンクで読み取ります。これは長いプロセスであり、それぞれの長さを報告します。
- 読み取りプロセスが終了すると (またはキャンセルされます。以下を参照してください)、ビジー インジケーターを非表示にします。
ユーザーが「キャンセル」ボタンを押すと、ファイルの読み取りプロセスがキャンセルされます。
- 前の操作がまだ進行中のときにユーザーが「実行」ボタンを押すと、前の操作はキャンセルされ、新しい操作が開始されます。
自然な制御フローが簡単に見られる同期操作の単純なシーケンスの外観を維持しながら、最小限のグルー コードでこれを実装する方法を探しています。
主な問題は、内側のオブザーバブル (リーダー) が終了したときにビジー インジケーターを隠しているようです。外側のオブザーバブルは「実行」クリック イベント シーケンスです。
これまでのところ、プログラムの主要部分の次の 2 つのバージョンを考え出しました。
加入者ベース:
IObservable<byte[]> xs =
runClick
.Do((Action<EventPattern<EventArgs>>)(_ => ShowBusy(true)))
.Do(_ => ShowError(false))
.Select(_ =>
reader
.TakeUntil(cancelClick)
.Publish(ob =>
{
ob.Subscribe(
b => { },
ex =>
{
Console.WriteLine("ERROR: " + ex);
ShowBusy(false);
ShowError(true);
},
() => ShowBusy(false));
return ob;
}))
.Switch();
xs = xs.Catch<byte[], Exception>(e => xs);
および連結ベース:
IObservable<byte[]> xs =
runClick
.Do((Action<EventPattern<EventArgs>>)(_ => ShowBusy(true)))
.Do(_ => ShowError(false))
.Select(_ =>
reader
.TakeUntil(cancelClick)
.DoAfter(() =>
ShowBusy(false)))
.Switch();
xs = xs.Catch<byte[], Exception>(e =>
{
Console.WriteLine("ERROR: " + e);
ShowBusy(false);
ShowError(true);
return xs;
});
1 つのカスタム メソッドでDoAfter
public static IObservable<T> DoAfter<T>(this IObservable<T> observable, Action action)
{
IObservable<T> appended = observable.Concat(
Observable.Create<T>(o =>
{
try
{
action();
}
catch (Exception ex)
{
o.OnError(ex);
return Disposable.Empty;
}
o.OnCompleted();
return Disposable.Empty;
}));
return appended;
}
(アプリケーション コード全体はこちらを参照してください)。
どちらの実装も私を完全に満足させるものではありません。最初の実装はかなり冗長で、2 番目の実装では、標準的な手段でカバーされると予想されるかなり単純なタスクに対して独自のメソッドを作成する必要があります。何か不足していますか?