548 Stimmen

Wie schreibe ich die Wiederholungslogik am saubersten?

Gelegentlich muss ich einen Vorgang mehrmals wiederholen, bevor ich aufgeben kann. Mein Code ist wie:

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

Ich würde dies gerne in eine allgemeine Wiederholungsfunktion umschreiben:

TryThreeTimes(DoSomething);

Ist dies in C# möglich? Wie würde der Code für die TryThreeTimes() Methode?

15voto

Brian Punkte 1816

Zulassen von Funktionen und Wiederholungsmeldungen

public static T RetryMethod<T>(Func<T> method, int numRetries, int retryTimeout, Action onFailureAction)
{
 Guard.IsNotNull(method, "method");            
 T retval = default(T);
 do
 {
   try
   {
     retval = method();
     return retval;
   }
   catch
   {
     onFailureAction();
      if (numRetries <= 0) throw; // improved to avoid silent failure
      Thread.Sleep(retryTimeout);
   }
} while (numRetries-- > 0);
  return retval;
}

14voto

csharptest.net Punkte 58070

Sie können auch den Ausnahmetyp hinzufügen, bei dem Sie es erneut versuchen möchten. Handelt es sich zum Beispiel um eine Timeout-Ausnahme, die Sie wiederholen möchten? Eine Datenbank-Ausnahme?

RetryForExcpetionType(DoSomething, typeof(TimeoutException), 5, 1000);

public static void RetryForExcpetionType(Action action, Type retryOnExceptionType, int numRetries, int retryTimeout)
{
    if (action == null)
        throw new ArgumentNullException("action");
    if (retryOnExceptionType == null)
        throw new ArgumentNullException("retryOnExceptionType");
    while (true)
    {
        try
        {
            action();
            return;
        }
        catch(Exception e)
        {
            if (--numRetries <= 0 || !retryOnExceptionType.IsAssignableFrom(e.GetType()))
                throw;

            if (retryTimeout > 0)
                System.Threading.Thread.Sleep(retryTimeout);
        }
    }
}

Sie können auch feststellen, dass alle anderen Beispiele ein ähnliches Problem mit der Prüfung auf Wiederholungen == 0 haben und entweder unendlich viele Wiederholungen durchführen oder keine Ausnahmen auslösen, wenn ein negativer Wert angegeben wird. Auch Sleep(-1000) wird in den obigen Catch-Blöcken fehlschlagen. Es hängt davon ab, wie "dumm" man die Leute erwartet, aber defensive Programmierung schadet nie.

12voto

Fabian Bigler Punkte 9589

Die Antwort von LBushkin wurde auf die neueste Art und Weise umgesetzt:

    public static async Task Do(Func<Task> task, TimeSpan retryInterval, int maxAttemptCount = 3)
    {
        var exceptions = new List<Exception>();
        for (int attempted = 0; attempted < maxAttemptCount; attempted++)
        {
            try
            {
                if (attempted > 0)
                {
                    await Task.Delay(retryInterval);
                }

                await task();
                return;
            }
            catch (Exception ex)
            {
                exceptions.Add(ex);
            }
        }
        throw new AggregateException(exceptions);
    }

    public static async Task<T> Do<T>(Func<Task<T>> task, TimeSpan retryInterval, int maxAttemptCount = 3)
    {
        var exceptions = new List<Exception>();
        for (int attempted = 0; attempted < maxAttemptCount; attempted++)
        {
            try
            {
                if (attempted > 0)
                {
                    await Task.Delay(retryInterval);
                }
                return await task();
            }
            catch (Exception ex)
            {
                exceptions.Add(ex);
            }
        }
        throw new AggregateException(exceptions);
    }  

und sie zu nutzen:

await Retry.Do([TaskFunction], retryInterval, retryAttempts);

während die Funktion [TaskFunction] kann entweder Task<T> oder einfach Task .

9voto

Anders Skovborg Punkte 224

Einfach halten mit C# 6.0

public async Task<T> Retry<T>(Func<T> action, TimeSpan retryInterval, int retryCount)
{
    try
    {
        return action();
    }
    catch when (retryCount != 0)
    {
        await Task.Delay(retryInterval);
        return await Retry(action, retryInterval, --retryCount);
    }
}

9voto

Erik Bergstedt Punkte 912

Polly verwenden

https://github.com/App-vNext/Polly-Samples

Hier ist eine generische Wiederholung, die ich mit Polly verwende

public T Retry<T>(Func<T> action, int retryCount = 0)
{
    PolicyResult<T> policyResult = Policy
     .Handle<Exception>()
     .Retry(retryCount)
     .ExecuteAndCapture<T>(action);

    if (policyResult.Outcome == OutcomeType.Failure)
    {
        throw policyResult.FinalException;
    }

    return policyResult.Result;
}

Verwenden Sie es so

var result = Retry(() => MyFunction()), 3);

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X