范围可以用“using”来阻止吗?关键字对异常有反应吗?

发布于 2024-08-11 11:50:01 字数 1116 浏览 7 评论 0原文

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

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

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

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 的所有“使用”范围块中进行 try/catch。有没有办法可以容纳“ReportFailure()”调用并仍然利用“using”范围块?

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

谢谢!

I have the need to do some logging within my code. I'm required to use an internal company-developed library to record some information. Here's how it works.

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

To ensure that Stop() is always called, I created a wrapper class that allows a clean "using" block.

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

it's worked well so far. However, there's a change my company is requiring, that would look something like this on the original code:

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.

I'd like to avoid try/catches in all my 'using' scope blocks with RecorderWrapper. Is there a way I can accomodate the "ReportFailure()" call and still leverage the 'using' scope block?

Specifically, I want everyone on my team to "fall into a pit of success", i.e. make it easy to do the right thing. To me, this means making it really hard to forget to call recorder.Stop() or forget the try/catch.

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

水水月牙 2024-08-18 11:50:01

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

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

所以您的示例将是:

recorder.Record(DoSomeWork);

You might be able to create a method on the recorder to hide this:

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

So your example would then just be:

recorder.Record(DoSomeWork);
柒夜笙歌凉 2024-08-18 11:50:01

您总是可以尝试类似的方法:

280Z28 编辑:我在这里使用类似于 Stopwatch.StartNew()。将您的 Recorder 类设置为 IDisposable,并从 Dispose() 调用 Stop()。我认为没有比这更清楚的了。

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

You could always try something like:

Edit by 280Z28: I'm using a static StartNew() method here similar to Stopwatch.StartNew(). Make your Recorder class IDisposable, and call Stop() from Dispose(). I don't think it gets any more clear than this.

using (Recorder recorder = Recorder.StartNew())
{
    try
    {
        DoSomeWork();
    }
    catch (Exception ex)
    {
        recorder.ReportFailure(ex);
    }
}
金橙橙 2024-08-18 11:50:01

您可以继续使用现有的 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); }
}

You could continue to use the RecorderWrapper you have, but add a TryExecuting method that accepts a lambda of what you want to happen add runs it in a try/catch block. eg:

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); }
}
动听の歌 2024-08-18 11:50:01

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

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

不过,就我个人而言,我会坚持使用 try/catch - 这对将来的维护者来说更清楚- 并且它提供对异常的访问。

You could copy the pattern used by TransactionScope, and write a wrapper that must be actively completed - if you don't call Complete(), then the Dispose() method (which gets called either way) assumes an exception and does your handling code:

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

Personally, though, I'd stick with try/catch - it is clearer for maintainers in the future - and it provides access to the Exception.

悲歌长辞 2024-08-18 11:50:01

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

No, a using block is only syntactic sugar for a try/finally block. It doesn't deal with try/catch. At that point you're going to be left with handling it yourself since it looks like you need the exception for logging purposes.

能怎样 2024-08-18 11:50:01

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

所以,这:(

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

我认为,完全)等同于:

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

并且您可以将捕获物附加到 try 块的末尾。

编辑:

作为罗布解决方案的替代方案:

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

A using block is effectively a try/finally block that calls dispose on the object in question.

So, this:

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

is (i think, exactly) equivalent to this:

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

And you can tack your catches onto the end of the try block.

Edit:

As an alternative to Rob's solution:

Recorder recorder = Recorder.StartNew()
try
{
    DoSomeWork();
}
catch (Exception ex)
{
    recorder.ReportFailure(ex);
}
finally
{
    recorder.Dispose();
}
总以为 2024-08-18 11:50:01

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

嗯,我已经需要做一些与此模式非常相似的事情,lambda、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();
      }
   }
}

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

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

然后,您可以通过提供 Wrap 函数的多个重载来扩展此模式,该重载接受多个异常处理程序委托。通常五个重载就足够了 - 您需要一次捕获五种以上不同类型的异常是很不寻常的。

Oops, I hadn't noticed that a new instance of Recorder was being created by StartTiming. I've updated the code to account for this. The Wrap function now no longer takes a Recorder parameter but instead passes the recorder it creates as an argument to the action delegate passed in by the caller so that the caller can make use of it if needed.

Hmmm, I've needed to do something very similar to this pattern, lambdas, the Action delegate and closures make it easy:

First define a class to do the wrapping:

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();
      }
   }
}

Now, use like so:

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();
   });

One question though - is it really desired that the catch handler swallows the exception without rethrowing it? This would usually be a bad practice.

BTW, I'll throw in an addition to this pattern which can be useful. Although, it doesn't sound like it applies to what you're doing in this instance: Ever wanted to do something like the above where you want to wrap some code with a set of startup actions and completion actions but you also need to be able to code some specific exception handling code. Well, if you change the Wrap function to also take an Action delegate and constrain T to Exception, then you've got a wrapper which allows user to specify the exception type to catch, and the code to execute to handle it, e.g.:

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();
      }
   }
}

To use.. (Note you have to specify the type of exception, as it obviously cannot be inferred. Which is what you want):

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

You can then extend this pattern by providing multiple overloads of the Wrap function which take more than one exception handler delegate. Usually five overloads will be sufficient - it's pretty unusual for you to need to catch more than five different types of exceptions at once.

西瑶 2024-08-18 11:50:01

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

Don't add another level of indirection. If you need to catch the Exception, use try..catch..finally and call Dispose() in the finally block.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文