過去記事インデックス
- Reactive Extensions再入門 その1
- Reactive Extensions再入門 その2「IObservableインターフェースとIObserverインターフェース」
- Reactive Extensions再入門 その3「IObservableのファクトリメソッド」
- Reactive Extensions再入門 その4「Timer系のファクトリメソッド」
- Reactive Extensions再入門 その5「HotとCold」
- Reactive Extensions再入門 その6「HotなIObservableを作成するファクトリ」
- Reactive Extensions再入門 その7「LINQスタイルの拡張メソッド」
- Reactive Extensions再入門 その8「SkipとTakeメソッド」
- Reactive Extensions再入門 その9「Skip + Take + Repeat = ドラッグ」
- Reactive Extensions再入門 その10「Doメソッド」
- Reactive Extensions再入門 その11「Catchメソッド」
はじめに
前回は、例外処理のtry catchと同じ要領で使えるCatchメソッドを紹介しました。今回はtry catchときたらfinallyということでFinallyメソッドの紹介を行います。
Finallyメソッド
Finallyメソッドは名前の通り例外処理のfinally句と同じ動きをします。Reactive ExtensionsのIObservable
IObservable<TSource> Finally(Action action)
まずは、正常にシーケンスの処理が終わる場合のコード例を下記に示します。
var source = new Subject<string>(); source // 終了時に必ず呼ばれる処理 .Finally(() => Console.WriteLine("Finally")) // 購読 .Subscribe( s => Console.WriteLine("OnNext {0}", s), ex => Console.WriteLine("OnError {0}", ex), () => Console.WriteLine("OnCompleted")); // 2つ値を発行したあと、終了する。 source.OnNext("A"); source.OnNext("B"); source.OnCompleted();
実行結果を下記に示します。
OnNext A OnNext B OnCompleted Finally
Finallyが、全ての処理の最後に呼ばれていることが確認出来ます。次に、Catchメソッドを使用して例外を処理したケースのコードを下記に示します。
var source = new Subject<string>(); source // sourceから例外が発生したらErrorという文字列を後続へ流す .Catch(Observable.Return("Error")) // 終了時に必ず呼ばれる処理 .Finally(() => Console.WriteLine("Finally")) // 購読 .Subscribe( s => Console.WriteLine("OnNext {0}", s), ex => Console.WriteLine("OnError {0}", ex), () => Console.WriteLine("OnCompleted")); // 2つ値を発行したあと、例外を発生させる。 source.OnNext("A"); source.OnNext("B"); source.OnError(new Exception("例外"));
実行結果を下記に示します。
OnNext A OnNext B OnNext Error OnCompleted Finally
この場合もCatchメソッドでエラーから復帰しているため、OnCompletedの後にFinallyが呼ばれていることが確認できます。次に、Catchメソッドを使用せずにSubscribeのOnErrorで例外処理をした場合の動作確認をするコードを下記に示します。
var source = new Subject<string>(); source // 終了時に必ず呼ばれる処理 .Finally(() => Console.WriteLine("Finally")) // 購読 .Subscribe( s => Console.WriteLine("OnNext {0}", s), ex => Console.WriteLine("OnError {0}", ex), () => Console.WriteLine("OnCompleted")); // 2つ値を発行したあと、例外を発生させる。 source.OnNext("A"); source.OnNext("B"); source.OnError(new Exception("例外"));
実行結果を下記に示します。
OnNext A OnNext B OnError System.Exception: 例外 Finally
この場合も、OnErrorなどのSubscribeで行われる処理の後にFinallyで指定した処理が実行されていることがわかります。最後に、OnErrorでもCatchメソッドでも例外を処理しなかった場合の動作を確認するコードを下記に示します。
var source = new Subject<string>(); var subscriber = source // 終了時に必ず呼ばれる処理 .Finally(() => Console.WriteLine("Finally")) // 購読(エラー処理無し) .Subscribe( s => Console.WriteLine("OnNext {0}", s)); // 2つ値を発行したあと、例外を発生させる。 source.OnNext("A"); source.OnNext("B"); try { // Reactive Extensionsのシーケンスの処理内で例外が処理されないため例外がスローされる source.OnError(new Exception("例外")); } catch { // 例外が発生した場合はログを出して握りつぶす Console.WriteLine("catch句"); } // 購読を解除する Console.WriteLine("subscriber.Dispose()"); subscriber.Dispose();
今回は、OnErrorでもCatchメソッドでも例外を処理していないためsource.OnError(new Exception(“例外”))の部分で例外が発生します。そのためtry catchで例外処理を記載しています。また、例外処理のあとで、SubscribeしたものをDisposeして購読を解除しています。このコードの実行結果を下記に示します。
OnNext A OnNext B catch句 subscriber.Dispose() Finally
上記の実行結果からわかるように、未処理の例外がある場合は、何もしないとFinallyメソッドで渡したアクションが実行されません。明示的にコードでDisposeをすることでFinallyで渡したアクションが実行されます。
Observable.Usingメソッド
Finallyメソッドでリソースの解放を行う処理を記述している場合にちらっと頭をよぎってほしいメソッドがあります。Observable.Usingメソッドです。Usingメソッドは、名前の通りusingブロックのようにリソースを確実に解放するために使用するメソッドになります。メソッドのシグネチャは、第一引数にFunc
// サンプルのダミーリソースクラス class SampleResource : IDisposable { // データを取得する public IObservable<string> GetData() { return Observable.Create<string>(o => { o.OnNext("one"); o.OnNext("two"); o.OnNext("three"); o.OnCompleted(); return Disposable.Empty; }); } // 解放処理 public void Dispose() { Console.WriteLine("Resource.Dispose called"); } }
このクラスを使用してUsingメソッドの動作確認を行います。Usingメソッドの使用例を下記に示します。
// SampleResource(IDisposableを実装)を使用してデータを取得する var source = Observable.Using( () => new SampleResource(), sr => sr.GetData()); // 購読 source.Subscribe( i => Console.WriteLine("OnNext({0})", i), ex => Console.WriteLine("OnError({0})", ex.Message), () => Console.WriteLine("Completed()"));
UsingメソッドでSampleResourceクラスの作成と、SampleResourceクラスを使ってIObservable
OnNext(one) OnNext(two) OnNext(three) Completed() Resource.Dispose called
実行結果からもわかるように、最後に、SampleResourceクラスのDisposeが呼ばれていることがわかります。Usingメソッドを使用できる場合はFinallyよりもこちらを使った方が漏れが無くて良いと思います。