Created
October 3, 2025 10:12
-
-
Save musoftware/dc5092a81fda4092f6119a9b2345b346 to your computer and use it in GitHub Desktop.
Generic Retry Handler Class in C#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| public class Program | |
| { | |
| private static readonly Random _opRandom = new Random(); | |
| public static void Main(string[] args) | |
| { | |
| Console.WriteLine("--- Running Action (void method) Example ---"); | |
| var serviceRetryPolicy = new RetryHandler(retries: 4, baseDelaySeconds: 0.5); | |
| try | |
| { | |
| serviceRetryPolicy.Execute(ConnectToFlakyService); | |
| } | |
| catch (Exception e) | |
| { | |
| Console.WriteLine($"[FATAL] Could not connect to service: {e.GetType().Name}"); | |
| } | |
| Console.WriteLine("\n--- Running Func<T> (method with return value) Example ---"); | |
| var apiRetryPolicy = new RetryHandler(retries: 5, baseDelaySeconds: 1.0); | |
| try | |
| { | |
| string result = apiRetryPolicy.Execute(FetchDataFromUnstableApi); | |
| Console.WriteLine($"[SUCCESS] Final result: {result}"); | |
| } | |
| catch (Exception e) | |
| { | |
| Console.WriteLine($"[FATAL] Could not fetch data: {e.GetType().Name}"); | |
| } | |
| } | |
| public static void ConnectToFlakyService() | |
| { | |
| Console.WriteLine("Attempting to connect to service..."); | |
| if (_opRandom.NextDouble() < 0.75) // 75% chance of failure | |
| { | |
| throw new InvalidOperationException("Service is temporarily unavailable."); | |
| } | |
| Console.WriteLine("Service connection successful!"); | |
| } | |
| public static string FetchDataFromUnstableApi() | |
| { | |
| Console.WriteLine("Attempting to fetch data from API..."); | |
| if (_opRandom.NextDouble() < 0.80) // 80% chance of failure | |
| { | |
| throw new TimeoutException("API call timed out."); | |
| } | |
| Console.WriteLine("Data fetched successfully!"); | |
| return "{\"data\": \"some important info\"}"; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Threading; | |
| public class RetryHandler | |
| { | |
| private readonly int _retries; | |
| private readonly double _baseDelaySeconds; | |
| private readonly double _backoffFactor; | |
| private readonly double _jitter; | |
| private static readonly Random _random = new Random(); | |
| public RetryHandler(int retries = 3, double baseDelaySeconds = 1.0, double backoffFactor = 2.0, double jitter = 0.1) | |
| { | |
| _retries = retries; | |
| _baseDelaySeconds = baseDelaySeconds; | |
| _backoffFactor = backoffFactor; | |
| _jitter = jitter; | |
| } | |
| // Overload for methods that don't return a value (void) | |
| public void Execute(Action action) | |
| { | |
| double currentDelay = _baseDelaySeconds; | |
| for (int attempt = 0; attempt < _retries; attempt++) | |
| { | |
| try | |
| { | |
| action(); | |
| return; // Success | |
| } | |
| catch (Exception e) | |
| { | |
| if (attempt == _retries - 1) | |
| { | |
| Console.WriteLine($"[ERROR] Action failed after {_retries} retries."); | |
| throw; // Re-throw the last exception | |
| } | |
| double sleepTime = currentDelay + _random.NextDouble() * _jitter; | |
| Console.WriteLine($"[WARN] Attempt {attempt + 1}/{_retries} failed: {e.Message}. Retrying in {sleepTime:F2} seconds..."); | |
| // Thread.Sleep expects milliseconds | |
| Thread.Sleep((int)(sleepTime * 1000)); | |
| currentDelay *= _backoffFactor; | |
| } | |
| } | |
| } | |
| // Generic overload for methods that return a value | |
| public T Execute<T>(Func<T> function) | |
| { | |
| double currentDelay = _baseDelaySeconds; | |
| for (int attempt = 0; attempt < _retries; attempt++) | |
| { | |
| try | |
| { | |
| return function(); // Success | |
| } | |
| catch (Exception e) | |
| { | |
| if (attempt == _retries - 1) | |
| { | |
| Console.WriteLine($"[ERROR] Function failed after {_retries} retries."); | |
| throw; | |
| } | |
| double sleepTime = currentDelay + _random.NextDouble() * _jitter; | |
| Console.WriteLine($"[WARN] Attempt {attempt + 1}/{_retries} failed: {e.Message}. Retrying in {sleepTime:F2} seconds..."); | |
| Thread.Sleep((int)(sleepTime * 1000)); | |
| currentDelay *= _backoffFactor; | |
| } | |
| } | |
| // This line is technically unreachable but required by the compiler | |
| throw new InvalidOperationException("Retry logic failed unexpectedly."); | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Centralized Logic: This pattern centralizes all the complex retry logic into one class. Instead of scattering try-catch loops throughout your code, you instantiate and use this handler, making your code much cleaner.
Strongly-Typed: By using Action for void methods and Func for methods with return types, you get compile-time safety and clarity about what kind of method you're executing.
Policy-Based Design: You can create different instances of RetryHandler with different configurations (e.g., a "fast retry" policy for internal services, a "slow retry" policy for external APIs) and apply them as needed.
Framework Independent: This is a plain C# solution that doesn't require any external libraries like Polly (though for very complex scenarios, Polly is an excellent choice). It's a great lightweight tool to have in your arsenal.