In-depth Analysis and Implementation of Elegant Retry Logic in C#

Nov 21, 2025 · Programming · 11 views · 7.8

Keywords: C# Retry Logic | Exception Handling | Delegates and Generics

Abstract: This article provides a comprehensive exploration of best practices for implementing retry logic in C#. By analyzing the limitations of traditional while-loop approaches, it presents a generic retry framework based on delegates and generics. The article details configuration of key parameters like retry intervals and maximum attempts, and explains core concepts including exception aggregation and thread sleeping. It also compares custom implementations with the Polly library, offering guidance for selecting appropriate solutions in different scenarios.

The Importance and Challenges of Retry Logic

In modern software development, external dependencies such as network requests and database operations often face transient failures. Proper retry mechanisms significantly enhance system fault tolerance and user experience. However, simple retry implementations often suffer from code duplication and inadequate exception handling.

Limitations of Traditional Approaches

Developers typically use while loops combined with exception catching to implement retry logic:

int retries = 3;
while(true) {
  try {
    DoSomething();
    break; // success!
  } catch {
    if(--retries == 0) throw;
    else Thread.Sleep(1000);
  }
}

While intuitive, this approach has clear drawbacks: code repetition, lack of flexibility, and loss of exception information.

Design and Implementation of Generic Retry Framework

A generic retry framework based on delegates and generics effectively addresses these issues. Here's a complete implementation:

public static class Retry
{
    public static void Do(
        Action action,
        TimeSpan retryInterval,
        int maxAttemptCount = 3)
    {
        Do<object>(() =>
        {
            action();
            return null;
        }, retryInterval, maxAttemptCount);
    }

    public static T Do<T>(
        Func<T> action,
        TimeSpan retryInterval,
        int maxAttemptCount = 3)
    {
        var exceptions = new List<Exception>();

        for (int attempted = 0; attempted < maxAttemptCount; attempted++)
        {
            try
            {
                if (attempted > 0)
                {
                    Thread.Sleep(retryInterval);
                }
                return action();
            }
            catch (Exception ex)
            {
                exceptions.Add(ex);
            }
        }
        throw new AggregateException(exceptions);
    }
}

Analysis of Core Design Points

Method Overload Design: The framework provides both Action and Func<T> overloads, supporting operations with and without return values respectively.

Parameterized Configuration: Retry interval retryInterval and maximum attempt count maxAttemptCount are provided as parameters, offering full flexibility.

Exception Aggregation: AggregateException collects all exceptions during attempts, ensuring complete debugging information.

Delayed Execution Optimization: The first attempt doesn't sleep, while subsequent attempts apply the retry interval, optimizing performance.

Practical Usage Examples

The framework usage is very concise:

// Operation without return value
Retry.Do(() => SomeFunctionThatCanFail(), TimeSpan.FromSeconds(1));

// Operation with return value
int result = Retry.Do(SomeFunctionWhichReturnsInt, TimeSpan.FromSeconds(1), 4);

Comparison with Third-party Libraries

Polly is a feature-rich .NET resilience library providing more complex strategies:

Policy
    .Handle<SqlException>(ex => ex.Number == 1205)
    .Or<ArgumentException>(ex => ex.ParamName == "example")
    .WaitAndRetry(3, _ => TimeSpan.FromSeconds(3))
    .Execute(DoSomething);

Polly supports conditional retries, circuit breakers, and other advanced features, suitable for complex scenarios. Custom implementations are better for simple, lightweight retry requirements.

System Design Considerations

At the system design level, retry logic must consider: reasonableness of retry strategies, avoiding cascading failures, monitoring and logging. Proper retry mechanisms are crucial components for building robust distributed systems.

Best Practice Recommendations

1. Set different retry strategies for different exception types

2. Consider implementing exponential backoff to avoid resource contention

3. Add appropriate logging in critical business scenarios

4. For long-running operations, consider implementing cancellation mechanisms

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.