Trying and Retrying in C#

I sometimes encounter a requirement where we want to execute some code and, if that method throws an exception, we want to retry executing it several times until some limit is reached.  In this scenario, the thrown exception was expected at times by the application, and could be ignored. The operation was something that failed frequently enough that it was necessary sometimes to retry until it succeeded (web services calls for example may fail due to intermittent connectivity issues).

Another scenario was that if the code failed, we wanted to wait a period of time before retrying. Below is what I came up with.

 

    1:             var service = new WebService();
    2:              //the type parameter is for the return type of 
    3:              //the method we are retrying (GetDataFromRemoteServer()
    4:              //returns a string in this example)
    5:              var retrier = new Retrier<string>();
    6:              //call the service up to 3 times in the event of failure
    7:              string result = retrier.Try(
    8:                  () => service.GetDataFromRemoteServer(), 3);
    9:   
   10:              //call the service up to 3 times, 
   11:              //wait 500ms if there is a failure
   12:              string result2 = retrier.TryWithDelay(
   13:                  ()=> service.GetDataFromRemoteServer(), 3, 500);

And here is the Retrier class.  It takes a lambda expression, tries to execute it, and if an exception is thrown, it swallows it and retries executing the code until maxRetries is reached.  This is only useful in scenarios where the exception that gets thrown is expected from time to time, and you just want to ignore it and re-execute your code.  One way to improve would be to take as a parameter an ExpectedException type, and if the caught exception does not match that, throw.  You should never swallow exceptional exceptions.  Also, if you try to execute the method the maximum number or retry times, and it is never successful, you probably want to go ahead and rethrow the exception in that case.  One scenario I think this might be useful is calling web services where the network connection is prone to failure, thus you want to retry in the event of a failure.

    1:  public class Retrier<TResult> 
    2:  {
    3:      public TResult Try(Func<TResult> func, 
    4:          int maxRetries)
    5:      {
    6:          TResult returnValue = default(TResult);
    7:          int numTries = 0;
    8:          bool succeeded = false;
    9:          while (numTries < maxRetries)
   10:          {
   11:              try
   12:              {
   13:                  returnValue = func();
   14:                  succeeded = true;
   15:              }
   16:              catch (Exception)
   17:              {
   18:                  //todo: figure out what to do here
   19:              }
   20:              finally
   21:              {
   22:                  numTries++;
   23:              }
   24:              if (succeeded)
   25:                  return returnValue;
   26:          }
   27:          return default(TResult);
   28:      }
   29:   
   30:      public TResult TryWithDelay(Func<TResult> func, int maxRetries, 
   31:          int delayInMilliseconds)
   32:      {
   33:          TResult returnValue = default(TResult);
   34:          int numTries = 0;
   35:          bool succeeded = false;
   36:          while (numTries < maxRetries)
   37:          {
   38:              try
   39:              {
   40:                  returnValue = func();
   41:                  succeeded = true;
   42:              }
   43:              catch (Exception)
   44:              {
   45:                  //todo: figure out what to do here
   46:              }
   47:              finally
   48:              {
   49:                  numTries++;
   50:              }
   51:              if (succeeded)
   52:                  return returnValue;
   53:              System.Threading.Thread.Sleep(delayInMilliseconds);
   54:          }
   55:          return default(TResult);
   56:      }
   57:  }