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