Collectives™ on Stack Overflow

Find centralized, trusted content and collaborate around the technologies you use most.

Learn more about Collectives

Teams

Q&A for work

Connect and share knowledge within a single location that is structured and easy to search.

Learn more about Teams

I have a chunk of code that does a "file put" operation inside a moderately complicated piece of retry and try/catch logic. This is working fine, it looks roughly like this:

while (...) {
try {
    FilePut(source, destination)
catch () {
   //Check exception type, possibly re-throw, possibly return, possibly increment
   //counters being checked by while loop

The details of this logic aren't the issue. But I'm realizing I have several other operations that also need to execute inside this same kind of logic and I want to avoid copy and pasting this logic around my application. I'd like to move it to a function and reuse that function. That function would have to take some sort of reference to the operation to be called, and the try logic would execute that operation (file put, file get, whatever).

This seems like a great place for a delegate, but the problem is, each of these operations has a different signature, so I'm not sure how to be able to write my above logic to be able to call "any" operation.

Is there a good way to do this in C#?

You need an Action delegate to hide all of the different signatures. Something like this:

public void DoAction(Action action)
    // Make the boilerplate code whatever you need it to be. 
    // I've just used a try catch for simplicity.
        // Call the action at the appropriate time.
        action();
    catch
        // Handle any exceptions as you wish.

Then, to be able to handle actions with different signatures, you can define a few overloads that take different types of the generic Action<T> delegates and all of the necessary arguments. These overloads will "curry" the generic action and its arguments to a plain Action:

public void DoAction<T>(Action<T> action, T arg1)
    DoAction(() => action(arg1));
public void DoAction<T1, T2>(Action<T1, T2> action, T1 arg1, T2 arg2)
    DoAction(() => action(arg1, arg2));
public void DoAction<T1, T2, T3>(Action<T1, T2, T3> action, T1 arg1, T2 arg2, T3 arg3)
    DoAction(() => action(arg1, arg2, arg3));
// etc...

To use:

public void SomeOtherMethod()
    DoAction(MethodThatTakesTwoInts, 42, 23);
    DoAction(MethodThatTakesAString, "Don't Panic!");

Also, if you not familiar with them, take a look at the related family of Func delegates as well for good measure.

@desigeek DoAction(()=>FilePut(source, dest)); DoAction(() => bla-bla1(x)); DoAction(() => bla-bla3(x1, x2, x3)); – Serj-Tm Jul 23, 2012 at 20:09 The signature differences are hidden inside the Action delegate. The Action delegates all have the same signature. – Raymond Chen Jul 23, 2012 at 20:09 I amended my answer to be a little more clear about currying various signatures down to an Action delegate. – FishBasketGordo Jul 23, 2012 at 20:17 Okay, your idea seems to work (thanks) although I'm not yet trying the third block of code you show above. Frankly, I'm a bit dumbfounded here. Why does this work? If one can pass "anything" to Action<> in c#, why do we have Action<...> with all its various overloads? Up till now I was thinking that whenever I saw Action<x,y> for example, it would only "work" when dealing with a method taking two of the right type of parameters.. but this is obviously not the "end of the story". What am I missing? – Michael Ray Lovett Jul 23, 2012 at 20:31 Very clean code, but it eats your exceptions. How do you know if action succeeded or not? Better put the if statement in the catch block, recurse if enough tries left and rethrow otherwise. – Kris Vandermotten Jul 23, 2012 at 20:22 @KrisVandermotten: You're right. I just considered "exception => try again" logic, as an start point to OP. – Ortiga Jul 23, 2012 at 20:37

Thanks for contributing an answer to Stack Overflow!

  • Please be sure to answer the question. Provide details and share your research!

But avoid

  • Asking for help, clarification, or responding to other answers.
  • Making statements based on opinion; back them up with references or personal experience.

To learn more, see our tips on writing great answers.