是否有任何事件会在出现异常时被触发?

5

在 .Net 框架中,是否有触发异常的事件?每当捕获到异常时,我需要记录它。因此,如果存在事件,我可以订阅该事件并在事件处理程序中记录异常。

10个回答

6

是的——UnhandledException事件存在于AppDomain对象上:

AppDomain.CurrentDomain.UnhandledException += YourHandler

请注意:仅在万不得已的情况下使用这些处理程序,最好在 trycatch 块中捕获异常,尽管这可能并不总是可行(例如,在第三方代码启动新线程时)。

此外,只有当异常未被处理时,才会触发此事件 - 据我所知,没有办法在没有将调试器附加到进程的情况下通知已捕获的事件。


“+1” 表示 “你应该只在万不得已的情况下使用这些处理程序”,我用它来记录异常、通知用户并重新启动应用程序。否则,你可能会遇到各种问题。 - Matt Warren
从文档中并不清楚,但似乎订阅此事件并不会给您任何机会阻止AppDomain的关闭。 - Steven Sudit

4
你应该考虑使用PostSharp进行一些面向切面编程,你可以编写一个简单的日志属性并将其应用于整个程序集。
你所需要的就像这样:
[Serializable]
public class LogAttribute : OnMethodInvocationAspect
{
   public override void OnInvocation(MethodInvocationEventArgs eventArgs)
   {
      try
      {
         eventArgs.Proceed();
      }
      catch(Exception ex)
      {
         // log exception here
      }
   }
}

并将其应用到您的程序集中:

[assembly: Log]
public class ...

这并非人人都能胜任,但我发现这是一种非常干净、整洁的方式,可以避免在我的类中编写大量样板代码,并使我有更多时间专注于与项目本身相关的功能。

更新:正如Kugel在评论中指出的那样,这将帮助您跟踪和记录在执行方法期间抛出的任何异常,但如果您想记录方法内部的状态,您需要做更多的工作。

例如,您可能仍然需要在方法内部使用try / catch块,以捕获对您的类有用的异常,甚至可以将它们包装在自定义异常对象中,以便您可以开始添加更多有用的信息,如错误代码等。只要您的自定义异常具有适当的设置“Message”属性的机制,例如:

public class DictionaryKeyNotValidException() : Exception
{
   public DictionaryKeyNotValidException(string key)
       : base(GetMessage(key))
   {   
   }

   public ErrorEnum ErrorCode { get { return ErrorEnum.InvalidDictionaryKey; } }

   private string GetMessage(string key)
   {
      return string.Format("ERROR {0} : Invalid dictionary key encountered {1}",
                           ErrorCode.GetHashCode(), key);
   }
}

如果您正在使用Log4Net,那么在您的日志属性中,您可以开始记录更有用的信息:

catch (Exception ex)
{
   // log error
   log.Error(ex);

   // handle exception, rethrow, etc.
   ...
}

抱歉,这变得有点冗长了..

如果你想在一个方法中记录状态,这个方法并不能帮到你。 - Kugel
1
在记录日志后一定要包含 throw; - Dan Bryant

2
除了Exception之外?我会在catch块中调用我的日志模块。类似于这样:
catch(YourException ex)
{
  LogMyException(ex, [otherParamsYouNeed]);
  //Other Exception Handling
}

如果您想记录某个操作的状态,无论成功或失败,都可以使用 finally{}


1
在WinForms中,您可以使用Application.ThreadException事件,更一般的是AppDomain.CurrentDomain.UnhandledException事件。
但请注意,在记录这些异常后,建议关闭应用程序。它可能不再处于稳定状态。

+1,这些处理程序是记录将导致应用程序终止的未处理异常的最佳位置。我认为这比“记录并重新抛出”更清晰,更接近故障点,因为.NET异常类已经保留了像StackTrace这样的上下文信息。 - Dan Bryant

1

AppDomain类有一个UnhandledException事件,但我认为您不能订阅抛出的任何异常。


0
在ASP.NET中,未处理的异常可以通过处理Page_Error事件来捕获页面级别上的异常,或者通过处理应用程序级别上的Application Error事件(在global.asax或IHttpModule实现中)来捕获。这些事件不会提供实际的异常信息,因此您需要调用服务器以获取异常信息:
    Exception ex = HttpContext.Current.Server.GetLastError();

0

Web的等效项是HttpApplication.Error http://msdn.microsoft.com/en-us/library/system.web.httpapplication.error.aspx - Jace Rhea

0

在theburningmonk的回答基础上,您还可以尝试使用log4PostSharp。这是预先构建的代码,使用PostSharp插入与log4net日志框架配合工作的日志记录代码。

除其他事项外,这将自动向您的方法添加捕获异常并记录它们的代码。

所有这些都取决于您是否愿意使用log4net日志框架或者您更喜欢自己实现。


0
在vb.net中,可以在catch语句中添加一个"When"限定符(例如'Catch Ex as ObjectDisposedException When Ex.Message.Contains("Sorry")')。这可用于日志记录(例如'Catch Ex as Exception When LoggingFunctionThatReturnsFalse(Ex)'不会捕获任何异常,但会记录它们所有),并且似乎是C#中有用的功能。不过,据我所知,该功能仅在vb.net中可用(如果使用此功能在vb.net中编译代码并反编译到C#中,不确定会发生什么)。

没错:这是CLR的一个特性,C#没有暴露出来。你可以通过捕获所有的ObjectDisposedException,检查它们的Message并在不符合条件时使用throw;重新抛出异常,从而实现类似的功能。不确定它在性能上如何比较,但毕竟它是一个异常,谁会在意呢? - Steven Sudit
@Steven Sudit:在调试时,未捕获的异常会在抛出它的位置中断执行;而捕获并重新抛出的异常会在重新抛出的位置中断执行。 - supercat
我不能否认在调试器下运行时会有差异,但我编写代码时不会出现未捕获的异常。在最坏的情况下,顶层catch块记录并退出。 - Steven Sudit
哦,至于C#支持,官方说法是C#和VB最终将能够做相同的事情。 - Steven Sudit
在调试时,我经常发现将异常保持未捕获状态很有用,这样调试器就可以直接跳转到异常发生的地方。在发布代码时,尽可能地将所有内容都包装在一个主 try/catch 块中(由于通信对象在其自己的线程上运行,所以我无法对从通信对象抛出的异常做太多处理)。 - supercat

0
你可以在Global.asax文件中的Application_Error事件处理程序中添加代码。
我使用这种方法来完成原帖中描述的操作,即记录错误并发送电子邮件通知。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接