在一个参数中组合Action和function

本文关键字:组合 Action function 参数 一个 | 更新日期: 2023-09-27 18:06:57

我有许多方法需要相同模式的日志记录。有些方法需要返回值,有些不需要。我创建了一个带有Action参数的方法,以避免复制粘贴所有的逻辑。它看起来像这样:

private void Execute(Action action)
{
   Logger.Start();
   try
   {
      action();
   }
   catch(Exception exception)
   {
      Logger.WriteException();
      throw;
   }
   finally
   {
       Logger.Finish();
   }
}

现在我有一些这样的调用

public void DoSomething(string parameter)
{
    Execute(() => GetProvider(parameter).DoSomething());
}

但是我需要一些返回值的函数。最好的方法是什么?我现在已经找到了两个:

1)用Func 创建Execute方法的副本
private T Execute<T>(Func<T> action)
{
   Logger.Start();
   try
   {
      return action();
   }
   catch(Exception exception)
   {
      Logger.WriteException();
      throw;
   }
   finally
   {
       Logger.Finish();
   }
}

此方法有效,但也有一些复制粘贴。

2)欺骗参数为Action:
public Result DoSomething(string parameter)
{
    Result result = null;
    Execute(() => result = GetProvider(parameter).DoSomething());
    return result;
}

不需要复制粘贴,但看起来不太好。

是否有一种方法可以连接Action和Func以避免任何这些方法,或者可能有另一种方法来实现相同的结果?

在一个参数中组合Action和function

第三种选择是仍然重载Execute,但使Action版本根据Func版本工作:

private void Execute(Action action)
{
    // We just ignore the return value here
    Execute(() => { 
        action();
        return 0; 
    });
}

当然,如果void更像一个"真正的"类型(如f#等中的Unit),那么这一切都会更简单,在这一点上,我们可以只使用Task<T>而不是TaskTask<T>

创建Execute的副本,将Func转换为Action。您只需要编写一次丑陋的代码,并且您最终不会得到Execute方法的完整的第二个副本:

private T Execute<T>(Func<T> func)
{
    T result = default(T);
    this.Execute(() => { result = func(); });
    return result;
}
...
public Result DoSomething(string parameter)
{
    return Execute(() => GetProvider(parameter).DoSomething());
}

这是另一个选项。与其让日志框架调用实际代码,不如让实际代码调用日志框架。类似这样的操作就可以达到目的(大大简化)。

public class LoggerScope : IDisposable {
    private bool disposed;
    public LoggerScope() {
        Logger.Start();
    }
    public void Dispose() {
        if(!disposed) {
            Logger.Finish();
            disposed = true;
        }
    }
}

用法如下:

        using(var scope = new LoggerScope()) {
            // actual code goes here
        }

通过在代码的顶层捕获和记录异常来单独处理异常。

优势:

  • 避免了到处都需要lambdas,因此异常堆栈跟踪更干净。
  • 您可以在LoggerScope类中添加任意上下文数据,例如GUID,时间戳,逻辑任务描述文本。