かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

Reactive Extensions再入門 その35「駄目ならやり直す!を実現するRetryメソッド」

過去記事インデックス

はじめに

前回のOnErrorResumeNextメソッドでは、エラーの場合は別の方法で再開するという処理をやりました。今回は、同じことを愚直にやり続けるRetryメソッドです!

Retryメソッド

ここではRetryメソッドについて説明します。Retryメソッドは名前の通りエラーが起きた場合に、繰り返し同じ処理を行うメソッドです。メソッドのオーバーロードには、成功するまで無限に繰り返しリトライをするものと、引数でリトライ回数を指定するものがあります。メソッドのシグネチャを下記に示します。

public static IObservable<T> Retry<T>(this IObservable<T> source);
public static IObservable<T> Retry<T>(this IObservable<T> source, int retryCount);

まず、無限に繰り返すメソッドのオーバーロードを使ったコード例を下記に示します。

var retryCount = 0;
Observable
    // retryCountが3になるまでエラーになる
    .Create<string>(o =>
    {
        Console.WriteLine("Create method called: {0}", retryCount);
        if (retryCount == 3)
        {
            o.OnNext(retryCount.ToString());
            o.OnCompleted();
            return Disposable.Empty;
        }

        retryCount++;
        o.OnError(new InvalidOperationException(retryCount.ToString()));
        return Disposable.Empty;
    })
    // 成功するまでリトライする
    .Retry()
    // 購読
    .Subscribe(
        s => Console.WriteLine("OnNext: {0}", s),
        ex => Console.WriteLine("OnError: {0}", ex),
        () => Console.WriteLine("OnCompleted"));

Createメソッドで3回目までエラーになるIObservableを作成しています。それに対してRetryメソッドを呼び出して、結果を表示しています。実行結果を以下に示します。

Create method called: 0
Create method called: 1
Create method called: 2
Create method called: 3
OnNext: 3
OnCompleted

Createメソッドが4回呼ばれてエラーではない値が発行されるとSubscribeまで値が渡っていることが確認できます。このようにエラーが起きても何度もリトライします。
次にリトライ回数を指定した場合の動作を示すコード例を下記に示します。

var retryCount = 0;
Observable
    // retryCountが3になるまでエラーになる
    .Create<string>(o =>
    {
        Console.WriteLine("Create method called: {0}", retryCount);
        if (retryCount == 3)
        {
            o.OnNext(retryCount.ToString());
            o.OnCompleted();
            return Disposable.Empty;
        }

        retryCount++;
        o.OnError(new InvalidOperationException(retryCount.ToString()));
        return Disposable.Empty;
    })
    // 2回リトライする
    .Retry(2)
    // 購読
    .Subscribe(
        s => Console.WriteLine("OnNext: {0}", s),
        ex => Console.WriteLine("OnError: {0}", ex),
        () => Console.WriteLine("OnCompleted"));

先ほどと、ほぼ同じコードですがRetryメソッドの引数で2回までしかリトライをしないように指定しています。このコードの実行結果を以下に示します。

Create method called: 0
Create method called: 1
OnError: System.InvalidOperationException: 2

実行結果からわかるように2回目のリトライでも例外が発生しているため、SubscribeのOnErrorが呼び出されています。おそらく、現実的にはリトライ回数を指定するオーバーロードを使うことが多くなると思います。