tags:

views:

57

answers:

4

I was writing some try-catch blocks for various methods today, and thought to myself it would be good to have utility method which would automatically call the method again for a number of times specified in a parameter, at a certain time.

However, I thought to myself, the method/property etc which will cause an exception will be at the top of the stacktrace (do property calls get put on the stacktrace?) in a single threaded application (so an application with no code relating to threading). So I can simply get the method name at the top and dynamically call it again.

So I would have code like:

string s = StackTrace.GetFrame(0).GetMethodName; (I can't remember the exact syntax).

With this method, I can execute it using an activator or one of several other ways.

But in a multi-threaded application, I could have several methods firing at once and I wouldn't know which one finishes first/last. So I can't expect a method for which I write a try-catch block to be at the top of the stack.

How would I go about achieving this?

+4  A: 

Please don't do this. It's a really, really, really, really, really bad idea.

Maybe not as bad as deleting files randomly, if the hard drive runs out of room - but just about as bad.

Matt Cruikshank
+1  A: 

While I question the need for an auto retrying mechanism (does randomly retrying really help you out in so many situations that you need a utility method?) - using StackTrace and Reflection is, at best, a terribly complicated solution.

Not that I suggest that anyone actually use this code, but I'd probably go with a delegate based approach to this particular problem:

public static class Extensions {
    public static void Try(this Action a, int maxTries) {
       new (Func<bool>(() => { a(); return true; })).Try(maxTries);
    }

    public static TResult Try<TResult>(this Func<TResult> f, int maxTries) {
       Exception lastException = null;

       for (int i = 0; i < maxTries; i++) {
          try {
              return f();
          } catch (Exception ex) {
              lastException = ex;
          }
       }

       throw lastException;
    }
}

Usage is a bit unorthodox, but fairly clear I think:

// Set a property
new Action(() => myObject.Property = 5).Try(5);

// With a return value
var count = new Func<int>(() => myList.Count).Try(3);

You can't inline a lambda to a method, but you could have a somewhat fluent interface:

Utilities.Try(
   () => MyObject.Property = 5
).Repeat(5);

And multi line methods:

Utilities.Try(() => {
   MyObject.Property1 = 5;
   MyObject.Property2 = 6;
   MyObject.Property3 = 7;
}).Repeat(5);
Mark Brackett
A: 

Mark's code is probably better, but here's mine...

If you really want to do something like this, I'd use code something like this. Yes, you still have to manually call it, but your idea of indiscriminately retrying ALL excepting methods is a really, really bad idea.

public class TryAgain
{
    public delegate void CodeToTryAgain ();

    public static void Repeat<E>(int count, CodeToTryAgain code) where E : Exception
    {
        while (count-- > 0)
        {
            try
            {
                code();
                return;
            }
            catch (E ex)
            {
                Console.WriteLine("Caught an {0} : {1}", typeof(E).Name, ex.Message);
                // ignoring it!
            }
        }
    }
}

And then you'd call your failing method, ThrowTwice, or whatever you want to do, like this:

TryAgain.Repeat<MyException>(5, delegate()
{
    ThrowTwice();
});

In this example, the Repeat method will ignore all exceptions of type MyException, trying to call ThrowTwice up to 5 times...

You can add your own sleeping and time-outs, and whatever.

Matt Cruikshank
A: 

Thanks for the solutions guys.

I'll reconsider this idea if it is overcomplicated. The code samples themselves are interesting though.

dotnetdev