我需要在代码中进行一些日志记录。我需要使用公司内部开发的库来记录一些信息。这是它的工作原理。
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。
谢谢!
答案 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
。