在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,大家也可以用委托来封装其他许多逻辑:)