仕事では調査や資料作成が多くて、ここのところ C# でコード書いていない。たまには書かないと忘れちゃいそうだし、何かお題ないかな〜って探してたら、こんなのを発見。
- Peercastでどんな配信してるか可視化してくれるサイト作った。 - キモブロ
- Ruby の retry-handler が激しく便利そうなので Java で実装してみた - YoshioriのBlog
業務アプリでもリトライを実装することはまぁまぁあるので、C# でも実装してみるよ。
namespace RetryHandlerSample { using System; using System.Collections.Generic; using System.Linq; using System.Threading; public class RetryException : Exception { public RetryException(string message) : base(message) { } public RetryException(string message, Exception innerException) : base(message, innerException) { } } public static class RetryHandler { public static void Retry(int retryCount, Action action) { Retry<Exception>(retryCount, action); } public static void Retry<TException>(int retryCount, Action action) where TException : Exception { Retry<TException>(retryCount, action, 0); } public static void Retry(int retryCount, Action action, int sleepTime) { Retry<Exception>(retryCount, action, sleepTime); } public static void Retry<TException>(int retryCount, Action action, int sleepTime) where TException : Exception { Retry<int, TException>(retryCount, () => { action(); return -1; }, sleepTime); } public static TResult Retry<TResult>(int retryCount, Func<TResult> action) { return Retry<TResult, Exception>(retryCount, action); } public static TResult Retry<TResult>(int retryCount, Func<TResult> action, int sleepTime) { return Retry<TResult, Exception>(retryCount, action, sleepTime); } public static TResult Retry<TResult, TException>(int retryCount, Func<TResult> action) where TException : Exception { return Retry<TResult, TException>(retryCount, action, 0); } public static TResult Retry<TResult, TException>(int retryCount, Func<TResult> action, int sleepTime) where TException : Exception { if (retryCount < 0) throw new ArgumentOutOfRangeException("retryCount"); if (action == null) throw new ArgumentNullException("action"); if (sleepTime < 0) throw new ArgumentOutOfRangeException("sleepTime"); for (var i = retryCount + 1; 0 < i; i--) { try { return action(); } catch (Exception ex) { if ((0 < i) && (ex is TException)) { if (0 < sleepTime) { Thread.Sleep(sleepTime); } } else { throw new RetryException("リトライに失敗しました。", ex); } } } throw new RetryException("リトライに失敗しました。"); } } class Program { static void Main(string[] args) { // 3回失敗するメソッドの呼び出しを3回リトライする var result = RetryHandler.Retry(3, GenerateFaultMethod(3)); Console.WriteLine(result); // 3回失敗するメソッドの呼び出しを2回リトライする try { var result2 = RetryHandler.Retry(2, GenerateFaultMethod(3)); Console.WriteLine(result); } catch (RetryException ex) { Console.WriteLine(ex.Message); } Console.ReadLine(); } // 指定した回数だけ呼び出し失敗するメソッドを返す private static Func<string> GenerateFaultMethod(int count) { return () => { if (0 < count) { count--; throw new Exception(); } return "success"; }; } } }
捕捉する例外の型をタイプセーフに指定したかったので、ジェネリックメソッドにしてみたけど、なんかしっくりこない。かといって、Type だと Exception を継承したクラス以外も渡せてしまうし。まぁ、妥協しておこうかな。
生粋の LINQ 星人なら Reactive Extensions でやってしまうのかもしれないけどね。