在C#编程中通常会碰到许多重复劳动的情况,而面向对象编程的原则就是消除这些重复劳动,增加代码重用性。我们虽然有很多基本的模式去做抽象和封装,但还是会碰到一些看似不得已的重复劳动的情况。比如你在写一个数据访问层的类,按照规范,每个方法都需要加try-catch,就像这样:
public void Method1() { try { // logic... } catch (DataException ex) { // fuck the ex... } } public void Method2() { try { // logic... } catch (DataException ex) { // fuck the ex... } } public void MethodN()...
这部分代码似乎只能每次都手写,但真的是这样吗?
其实不然,别忘了C#有个很碉堡的东西叫委托,我们可以用Action或Func委托把数据库访问的逻辑封装起来,这样就只要定义一次try-catch的规则就OK了。就像这样:
不要求返回值的:
public void TryExecute(Action action, string funcName = null) { try { action(); } catch (Exception e) { Logger.Error(string.Format("{0}() Blow Up.", funcName), e); throw; } }
于是Method1、Method2等代码就可以修改为:
public void Method1() { TryExecute(() => { // Logic }, "Method1"); }
有没有感觉碉了很多?以此类推,我写了几个比较常用的TryExecute方法:
带返回值的:
public T TryExecute<T>(Func<T> func, string funcName = null) { try { return func(); } catch (Exception e) { Logger.Error(string.Format("{0}() Blow Up.", funcName), e); return default(T); } }
支持.NET 4.5 async await关键词的:
public async Task<T> TryExecuteAsync<T>(Func<Task<T>> func, string funcName = null) { try { return await func(); } catch (Exception e) { Logger.Error(string.Format("{0}() Blow Up.", funcName), e); return default(T); } }
自定义包裹类的,比如Request/Response模式:
public Response<T> TryExecute<T>(Func<Response<T>> func, string funcName = null) { try { return func(); } catch (Exception e) { Logger.Error(string.Format("{0}() Blow Up.", funcName), e); return new Response<T> { Item = default(T), Message = e.Message }; } }
所有带返回值的调用也很简单,就像这样:
public int Method1() { return TryExecute(() => { return 0; }, "Method1"); }
但是注意async的那只,需要指定一个async lambda表达式,就像这样:
public async Task<int> Method1() { return await TryExecuteAsync(async () => { return await SomeOperation(); }, "Method1"); }
同样的场景不仅仅局限于数据库访问或简化try-catch,大家也可以用委托来封装其他许多逻辑:)