Without much ado lets go straight to the code that needs to be refactored:
bool SomeMethod(long param) { // // some prefix code // try { //do specific job here return DoSpecificJob(param); } finally { // // some suffix code // } } Result SomeOtherMethod(string name, int count) { // // some prefix code // try { return DoOtherSpecificJob(name, count); } finally { // // some suffix code // } }The question is how we can bring prefix and suffix code from the example above in one place (method) without changing code logic.
The goal is to have these two methods rewritten like this:
bool SomeMethod(long param) { return DoSpecificJob(param); } Result SomeOtherMethod(string name, int count) { return DoOtherSpecificJob(name, count); }While another method will be created that executes prefix and suffix code.
There are several ways how to do that:
1. Create method that contains prefix and suffix code, accepts Delegate object and params object[] array
object ExecuteCommon(Delegate d, params object[] args) { // // prefix code // try { return d.DynamicInvoke(args); } finally { // // suffix code // } } bool SomeMethod_First(long param) { Delegate d = new Func<long, bool>((b) => SomeMethod(b)); return (bool)ExecuteCommon(d, new object[] {param}); } Result SomeOtherMethod_First(string name, int count) { Delegate d = new Func<string, int, Result>((n, c) => SomeOtherMethod(n, c)); return (Result)ExecuteCommon(d, new object[] { name, count }); }The approach looks nice but has several caveats. The problems here are: boxing (wrapping value types into reference types) and casting.
2. Move repeated code up on the call stack
This approach is possible if SomeMethod and SomeOtherMethod are on the same call stack level or called from the same method.
3. Create a generic method that accepts generic delegate and defines several parameters
TResult ExecuteCommon<T1,TResult>(Func<T1, TResult> func, T1 param1) { // // some prefix code // try { return func(param1); } finally { // // some suffix code // } } TResult ExecuteCommon<T1, T2, TResult>(Func<T1, T2, TResult> func, T1 param1, T2 param2) { // // some prefix code // try { return func(param1, param2); } finally { // // some suffix code // } } bool SomeMethod_Third(long param) { return ExecuteCommon<long, bool>((p) => SomeMethod(param), param); } Result SomeOtherMethod_Third(string name, int count) { return ExecuteCommon<string, int, Result>( (n, c) => SomeOtherMethod(n, c), name, count); }This method does not use casting and there is no boxing present when value type parameters are specified. However, the caveat is that you need to define multiple methods with variable type parameters count. In my opinion the third approach is the best although we have to define two methods that execute prefix/suffix code.
P.S. Another way of refactoring here is code generation: emitting code on the fly or using template tools like T4 templates in Visual Studio.
No comments:
Post a Comment