Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass code blocks (not full methods) as arguments in C#?

I'm building a messaging app in csharp (.net 4.0), my class has basic methods for sending/receiving messages:

void sendMessage( string msgBody, string properties);
object getNextMessage();
object getMessageById( string msgId);

Each of these methods depends on an underlying connection; if the connection is stale, I use try/catch and some retry logic to make additional attempts, something like this:

public object getNextMessage(){
   object nextMessage = null;
   int retryAttempts = 0;
   int MAX_ATTEMPTS = 3;

   while( retryAttempts < MAX_ATTEMPTS){
      retryAttempts++;
      try{
         nextMessage = connection.getMessage("queueName");
      }catch(Exception e){   
      }
   }
   return nextMessage;
}

Since the retry logic is generic, I want to avoid repeating the same code in each method. I want to create a common retry function and do something like this:

public object makeAttempt( CodeBlock codeBlock){
       while( retryAttempts < MAX_ATTEMPTS){
          retryAttempts++;
          try{
             return codeBlock.invoke()
          }catch(Exception e){   
          }
       }
       return null;
}

..I want to use makeAttempt like this, or something similar:

public object getNextMessage(){       
   makeAttempt() => {
      return connection.getMessage("queueName");
   }
}

I reviewed this, but it relates to passing entire functions as arguments, which I'm not doing. I also reviewed .net Lambda Expressions, but I'm not seeing a connection.

I haven't done much C# so forgive the n00b question :-)

like image 971
raffian Avatar asked Oct 17 '13 16:10

raffian


1 Answers

You're nearly there at the end - you just need to enclose the lambda expression in () as it's a method argument. You also need to use the return value from makeAttempt to provide a return value for your getNextMessage method. So:

public object getNextMessage(){       
   return makeAttempt(() => {
      return connection.getMessage("queueName");
   });
}

Or more simply, use an expression lambda:

public object getNextMessage(){       
   return makeAttempt(() => connection.getMessage("queueName"));
}

This is all assuming that CodeBlock is a delegate type, of course, e.g.

public delegate object CodeBlock();

You also need to change makeAttempt to call Invoke rather than invoke - C# is case-sensitive. I'd strongly urge you to follow .NET naming conventions, too, where methods are PascalCased instead of camelCased.

EDIT: As noted in comments, you could make this generic:

public T CallWithRetries<T>(Func<T> function)
{
    for (int attempt = 1; attempt <= MaxAttempts; attempt++)
    {
        try
        {
            return function();
        }
        catch(Exception e)
        {
            // TODO: Logging
        }
    }
    // TODO: Consider throwing AggregateException here
    return default(T);
}
like image 72
Jon Skeet Avatar answered Nov 18 '22 23:11

Jon Skeet