具有“using”关键字的范围块是否可以对异常做出反应?

时间:2009-11-16 19:23:32

标签: c# .net exception using

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

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。

谢谢!

8 个答案:

答案 0 :(得分:7)

您可以在录像机上创建一个隐藏此方法的方法:

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

所以你的例子就是:

recorder.Record(DoSomeWork);

答案 1 :(得分:3)

您可以尝试以下方式:

编辑280Z28:我在这里使用类似于Stopwatch.StartNew()的静态StartNew()方法。建立Recorder课程IDisposable,然后从Stop()致电Dispose()。我不认为它比这更清楚。

using (Recorder recorder = Recorder.StartNew())
{
    try
    {
        DoSomeWork();
    }
    catch (Exception ex)
    {
        recorder.ReportFailure(ex);
    }
}

答案 2 :(得分:3)

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

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

Inside RecorderWrapper:

public void TryExecuting(Action work)
{
    try { work(); }
    catch(Exception ex) { this.ReportFailure(ex); }
}

答案 3 :(得分:1)

您可以复制TransactionScope使用的模式,并编写一个必须主动完成的包装 - 如果您不调用Complete(),那么{{1方法(以任何方式调用)假定异常并处理代码:

Dispose()

但就个人而言,我坚持使用try / catch - 未来维护人员会更清楚 - 它可以访问using(Recorder recorder = Recorder.StartTiming()) { DoSomeWork(); recorder.Complete(); }

答案 4 :(得分:1)

不,使用块只是try / finally块的语法糖。它不涉及try / catch。此时,您将自己处理它,因为看起来您需要例外以进行日志记录。

答案 5 :(得分:1)

using block实际上是一个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();
}

答案 6 :(得分: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,那么您将拥有一个包装器,允许用户指定要捕获的异常类型,以及要执行以处理它的代码,例如:< / p>
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函数的多个重载来扩展此模式,该函数需要多个异常处理程序委托。通常五次重载就足够了 - 你需要同时捕获超过五种不同类型的异常是非常不寻常的。

答案 7 :(得分:0)

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