Reputation: 583
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
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
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
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