Maltor
Maltor

Reputation: 583

DRY With Different Try Statements and Identical Catch Statements

So I have the following block of code inside a method: (all variables are local)

// ...

try
{
    if (postXml != null)
        using (StreamWriter writer = new StreamWriter(req.GetRequestStream()))
            writer.Write(postXml.ToString());
}
catch (WebException ex)
{
    HttpWebResponse response = ex.Response as HttpWebResponse;
    if (response != null)
        result = HandleOtherResponse(response, out status);
    else result = HandleBadResponse(ex.ToString(), out status);
}
catch (Exception ex)
{
    result = HandleBadResponse(ex.ToString(), out status);
}

if (result == null)
{
    try
    {
        HttpWebResponse response = req.GetResponse() as HttpWebResponse;
        result = HandleOtherResponse(response, out status);
    }
    catch (WebException ex)
    {
        HttpWebResponse response = ex.Response as HttpWebResponse;
        if (response != null)
            result = HandleOtherResponse(response, out status);
        else result = HandleBadResponse(ex.ToString(), out status);
    }
    catch (Exception ex)
    {
        result = HandleBadResponse(ex.ToString(), out status);
    }
}

// ...

As you can see, the two try statements are different, but the two sets of catch statements are exactly the same. I've been trying to think of a way that it might be possible to not repeat myself here, but I haven't really thought of a way that wouldn't be significantly slower or just as terrible looking. Wondering if anyone has any ideas.

Upvotes: 7

Views: 939

Answers (3)

Reacher Gilt
Reacher Gilt

Reputation: 1813

You can do something with delegates and cover both try and catch blocks:

  static class Program
  {
      delegate void CodeBlock();

      internal delegate void ExceptionCatcher(Exception ex);


    private static void Main()
    {
        CodeBlock b = () => { Console.WriteLine("HELLO WORLD"); };
        CodeBlock error = () => { throw new Exception("Exception thrown"); };
        ExceptionCatcher silence = exception => { };
        ExceptionCatcher e = exception =>
            {
                var currentColor = Console.BackgroundColor;
                Console.BackgroundColor = ConsoleColor.Red;
                Console.WriteLine(exception.Message);
                Console.BackgroundColor = currentColor;
            };

        DRYRunner(b, e);
        DRYRunner(error , e);
        DRYRunner(error , silence);

        Console.ReadLine();
    }

    static void DRYRunner (CodeBlock block, ExceptionCatcher catcher)
    {
        try
        {
            block.Invoke();
        }
        catch (Exception ex)
        {
            catcher(ex);
        }
    }
}   

edit: Extending this out, we can create a class to help contain and associate code blocks with their possible exceptions and handlers. You could even create a class of common exception handlers and reference them accordingly, mixing them in with ad-hoc handlers:

 class ExceptionHandledDelegate
{
    public delegate void CodeBlock();

    public delegate void ExceptionCatcher(Exception ex);

    public Dictionary<Type, ExceptionCatcher> ExceptionHandlers;

    public CodeBlock codeBlock { get; set; }

    public void Run()
    {
        try
        {
            codeBlock.Invoke();
        }
        catch (Exception ex)
        {
            var mn = ex.GetType();
            if (ExceptionHandlers.Keys.Contains(mn))
            {
                ExceptionHandlers[mn](ex);
            }
            else throw; 
        }

    }
}
class CommonHandlers
{
    public static void ArgumentHandler(Exception ex)
    {
        Console.WriteLine("Handling an argument exception");
    }

    public static void DivZeroHandler(Exception ex)
    {
        Console.WriteLine("Please don't divide by zero. It upsets the universe.");
    }
}
static class Program
{

    private static void Main()
    {
        var ehd = new ExceptionHandledDelegate
        {
            codeBlock = () => { throw new ArgumentException("An argument exception has been thrown"); },
            ExceptionHandlers = new Dictionary<Type, ExceptionHandledDelegate.ExceptionCatcher>
            {
                {typeof (ArgumentException), CommonHandlers.ArgumentHandler},
                {typeof (DivideByZeroException ),CommonHandlers.DivZeroHandler},
                {typeof (Exception), exception => Console.WriteLine("An exception has been thrown")}
            }
        };
        ehd.Run();
        ehd.codeBlock = () => { throw new Exception("An exception has been thrown"); };
        ehd.Run();
        ehd.codeBlock = () =>{var denom = 0; Console.WriteLine(100 / denom);};
        ehd.Run();
        Console.ReadLine();
    }
}

Upvotes: 0

Haney
Haney

Reputation: 34892

One way would be to write a "safe" invocation method and pass a func to it:

public T SafeInvocation(Func<T> myMethod)
{
    T result = default(T);

    try
    {
        // Invoke method
        result = myMethod();
    }
    catch
    {
        // Do your common catch here
    }

    return result;
}

Build an additional overload for Action<T> so that you don't need to have a return type. Then you could invoke it elsewhere, passing methods to your method as arguments (Inception?):

SafeInvocation(() => 
{
    if (postXml != null)
        using (StreamWriter writer = new StreamWriter(req.GetRequestStream()))
            writer.Write(postXml.ToString());
}

Upvotes: 5

Steve Greatrex
Steve Greatrex

Reputation: 16009

You could pass an Action into a function that handles the exceptions:

private void HandleErrorsFor(Action action)
{
    try
    {
        action();
    }
    catch (Exception ex)
    {
        //repeated exception handling...
    {
}

//...

public void DoSomething()
{
    HandleErrorsFor(() => {
        //try block #1
    });

    HandleErrorsFor(() => {
        //try block #2
    });
}

It's a bit easier to read and avoids the repeated code.

Upvotes: 1

Related Questions