3

我需要在我的代码中进行一些日志记录。我需要使用公司内部开发的图书馆来记录一些信息。这是它的工作原理。

Recorder recorder = Recorder.StartTiming();
DoSomeWork();
recorder.Stop();  // Writes some diagnostic information.

为了确保 Stop() 总是被调用,我创建了一个允许干净的“使用”块的包装类。

using (RecorderWrapper recorderWrapper = new RecorderWrapper)  // Automatically calls Recorder.StartTiming() under the covers
{
   DoSomeWork();
}  // When the recorderWrapper goes out of scope, the 'using' statement calls recorderWrapper.Dispose() automatically - which calls recorder.Stop() under the covers

到目前为止效果很好。但是,我的公司需要进行更改,在原始代码上看起来像这样:

Recorder recorder = Recorder.StartTiming();
try
{
   DoSomeWork();
}
catch (Exception ex)
{
   recorder.ReportFailure(ex);  // Write out some exception details associated with this "transaction"
}
recorder.Stop();  // Writes some diagnostic information.

我想避免使用 RecorderWrapper 在我的所有“使用”范围块中尝试/捕获。有没有办法可以容纳“ReportFailure()”调用并仍然利用“使用”范围块?

具体来说,我希望我团队中的每个人都“陷入成功的陷阱”,即让做正确的事情变得容易。对我来说,这意味着很难忘记调用 recorder.Stop() 或忘记 try/catch。

谢谢!

4

8 回答 8

7

您也许可以在记录器上创建一个方法来隐藏它:

public void Record(Action act)
{
    try
    {
        this.StartTiming();
        act();
    }
    catch(Exception ex)
    {
        this.ReportFailure(ex);
    }
    finally
    {
        this.Stop();
    }
}

所以你的例子就是:

recorder.Record(DoSomeWork);
于 2009-11-16T19:31:47.003 回答
3

你总是可以尝试类似的东西:

由 280Z28 编辑:我在StartNew()这里使用类似于Stopwatch.StartNew(). 做你的RecorderIDisposable,然后Stop()Dispose(). 我认为没有比这更清楚的了。

using (Recorder recorder = Recorder.StartNew())
{
    try
    {
        DoSomeWork();
    }
    catch (Exception ex)
    {
        recorder.ReportFailure(ex);
    }
}
于 2009-11-16T19:32:30.540 回答
3

您可以继续使用您拥有的 RecorderWrapper,但添加一个 TryExecuting 方法,该方法接受您想要发生的事情的 lambda 添加在 try/catch 块中运行它。例如:

using (RecorderWrapper recorderWrapper = new RecorderWrapper)  // Automatically calls Recorder.StartTiming() under the covers
{
    recorderWrapper.TryExecuting(() => DoSomeWork());
}

RecorderWrapper 内部:

public void TryExecuting(Action work)
{
    try { work(); }
    catch(Exception ex) { this.ReportFailure(ex); }
}
于 2009-11-16T19:38:57.863 回答
1

您可以复制 使用的模式TransactionScope,并编写一个必须主动完成的包装器- 如果您不调用Complete(),则该Dispose()方法(无论哪种方式都被调用)假定一个异常并执行您的处理代码:

using(Recorder recorder = Recorder.StartTiming()) {
    DoSomeWork();
    recorder.Complete();
}

不过,就我个人而言,我会坚持使用 try/catch——这对未来的维护者来说会更清楚——它提供了对Exception.

于 2009-11-16T19:27:36.917 回答
1

不, using 块只是 try/finally 块的语法糖。它不处理尝试/捕获。那时你将不得不自己处理它,因为看起来你需要异常来记录日志。

于 2009-11-16T19:29:09.237 回答
1

using 块实际上是一个 try/finally 块,它在相关对象上调用 dispose。

所以这:

using(a = new A())
{
    a.Act();
}

是(我认为,完全)相当于这个:

a = new A();
try
{
    a.Act();
}
finally
{
    a.Dispose();
}

你可以将你的捕获物钉在 try 块的末端。

编辑:

作为 Rob 解决方案的替代方案:

Recorder recorder = Recorder.StartNew()
try
{
    DoSomeWork();
}
catch (Exception ex)
{
    recorder.ReportFailure(ex);
}
finally
{
    recorder.Dispose();
}
于 2009-11-16T19:29:43.807 回答
1

糟糕,我没有注意到 StartTiming 正在创建一个新的 Recorder 实例。我已经更新了代码来解决这个问题。Wrap 函数现在不再接受 Recorder 参数,而是将它创建的记录器作为参数传递给调用者传入的操作委托,以便调用者可以在需要时使用它。

嗯,我需要做一些与这种模式非常相似的事情,lambdas、Action 委托和闭包让它变得简单:

首先定义一个类来进行包装:

public static class RecorderScope
{
   public static void Wrap(Action<Recorder> action)
   {
      Recorder recorder = Recorder.StartTiming();
      try
      {
         action(recorder);
      }
      catch(Exception exception)
      {
         recorder.ReportFailure(exception);
      }
      finally
      {
         recorder.Stop();
      }
   }
}

现在,像这样使用:

RecorderScope.Wrap(
   (recorder) =>
   {
      // note, the recorder is passed in here so you can use it if needed -
      // if you never need it you can remove it from the Wrap function.
      DoSomeWork();
   });

但是有一个问题 - 是否真的希望 catch 处理程序吞下异常而不重新抛出它?这通常是一个不好的做法。

顺便说一句,我会在这个模式中添加一个有用的东西。虽然,它听起来并不适用于你在这种情况下所做的事情:曾经想做类似上面的事情,你想用一组启动动作和完成动作包装一些代码,但你还需要能够编写一些特定的异常处理代码。好吧,如果您将 Wrap 函数更改为也接受一个 Action 委托并将 T 约束为 Exception,那么您就有一个包装器,它允许用户指定要捕获的异常类型,以及要执行的代码来处理它,例如:

public static class RecorderScope
{
   public static void Wrap(Action<Recorder> action, 
      Action<Recorder, T1> exHandler1)
      where T1: Exception
   {
      Recorder recorder = Recorder.StartTiming();
      try
      {
         action(recorder);
      }
      catch(T1 ex1)
      {
         exHandler1(recorder, ex1);
      }
      finally
      {
         recorder.Stop();
      }
   }
}

要使用..(请注意,您必须指定异常的类型,因为它显然无法推断。这就是您想要的):

RecorderScope.Wrap(
   (recorder) =>
   {
      DoSomeWork();
   },
   (recorder, MyException ex) =>
   {
      recorder.ReportFailure(exception);
   });

然后,您可以通过提供多个 Wrap 函数的重载来扩展此模式,这些重载采用多个异常处理程序委托。通常五个重载就足够了——你需要一次捕获超过五个不同类型的异常是很不寻常的。

于 2009-11-16T20:05:58.603 回答
0

不要添加另一个级别的间接性。如果您需要捕获异常,请在块中使用try..catch..finally和调用。Dispose()finally

于 2009-11-16T19:30:39.967 回答