Managing Unhandled Exceptions in .NET

本文介绍了.NET 增强的异常处理功能,指出在代码中大量使用 try - catch 块会使代码杂乱。微软开发者建议根据应用类型(控制台、Windows 或 ASP.NET)使用.NET 的未处理异常事件处理程序,还给出了 C# 和 Visual Basic.NET 的示例及不同应用类型的处理方法。

Introduction

One of the things that impressed me when I first started learning .NET was its enhanced exception-handling functionality. By this I mean such features as easy access to the type of exception thrown, full stack trace and inner exceptions. This makes it easy to still get full information in those places where you just catch a top-level System.Exception. I find this convenient since, if you don't need to take specific action on a particular type of exception, it is tedious to have successive catch handlers for each type of exception that may be thrown. In any case, even if you do catch specific exceptions you usually also need to catch System.Exception just to cover yourself and prevent program crashes. Thus I find that I end up catching System.Exception all over the place in my code. A typical scenario is that in Windows Forms and ASP.NET Web Forms applications, all of my non-trivial event handlers end up containing try-catch System.Exception blocks.

The trouble is that this does still clutter up the code somewhat and doesn't really seem quite right. Is there a better way?

The better way

A couple of months ago someone posted a message on one of Microsoft's .NET newsgroups asking just this question. In having catch System.Exception all over the place what you're really doing is a "catch-all," i.e., you're trying to catch unhandled exceptions - exceptions that you don't know about. 

One of Microsoft's developers responded to the post and pointed out that we should not use exception blocks to catch unhandled exceptions. Instead, depending on our type of application, Console, Windows or ASP.NET, we should use one of .NET's unhandled exception event handlers. This means that we'll just have one error handler to handle all unhandled exceptions. When such an exception is generated we can provide the user with the option of continuing or aborting the application.

This is documented in the .NET Help but it doesn't really stand out in the various exception handling topics. You can find some discussion for ASP.NET in the article, Exception Management in .NET. In the discussion that follows I describe how to manage unhandled exceptions in Windows Forms applications.

Consider a simple Windows Forms application containing a single form with two buttons and a text box. The discussion is in C# but a full example follows in both C# and Visual Basic .NET.

 

The Add button just adds my name to the text box. The Remove button clears it. Suppose the Add button throws an exception. Hitherto, I would have done something like this.

private void btnAdd_Click(object sender, System.EventArgs e) { try { txtName.Text = "Kevin"; throw new InvalidOperationException("Invalid operation."); } catch (System.Exception ex) { DisplayError(ex); } } 

(Normally, I would only do this if the Add function were performing some elaborate operation - typically calling other non-trivial routines - but I'm here just illustrating the process.)

But by writing an unhandled exception event handler delegate we can dispense with the try-catch block above. The signature for the delegate looks like this.

public static void Application_ThreadException( object sender, ThreadExceptionEventArgs e) { // Handle exception.  // The exception object is contained in e.Exception. }

And this is how we hook it up.

static void Main() { // Subscribe to thread (unhandled) exception events // (Alternatively, could do this in Form_Load) Application.ThreadException += new ThreadExceptionEventHandler( Application_ThreadException); // Load the form Application.Run(new Form1()); }

So the Add function can now look like this.

private void btnAdd_Click(object sender, System.EventArgs e) { txtName.Text = "Kevin"; throw new InvalidOperationException("Invalid operation."); }

Example

Here is the complete example in C# and Visual Basic .NET (with Windows Form designer generated code omitted). I have also moved the exception-handling event into an assembly-wide class so that it can be accessed by other forms. When an unhandled exception is received an AbortIgnoreRetry dialog is displayed giving a full description of the error. Of course, in a production version we would just inform the user of an application error and log the details.

 C# Implementation

using System; using System.Threading; public class Form1 : System.Windows.Forms.Form { // ...(omitted) ///  /// The main entry point for the application. ///  [STAThread] static void Main() { // Subscribe to thread (unhandled) exception events ThreadExceptionHandler handler = new ThreadExceptionHandler(); Application.ThreadException += new ThreadExceptionEventHandler( handler.Application_ThreadException); // Load the form Application.Run(new Form1()); } ///  /// Adds default name to text box. ///  private void btnAdd_Click(object sender, EventArgs e) { // Generate handled exception //Add();  // Generate unhandled exception AddWithUnhandledException(); } ///  /// Removes name from text box. ///  private void btnRemove_Click(object sender, EventArgs e) { txtName.Clear(); } ///  /// Adds default name to text box. /// Throws handled exception. ///  private void Add() { try { txtName.Text = "Kevin"; throw new InvalidOperationException( "Invalid operation."); } catch (System.Exception ex) { DisplayError(ex); } } ///  /// Adds default name to text box. /// Throws unhandled exception. ///  private void AddWithUnhandledException() { txtName.Text = "Kevin"; throw new InvalidOperationException( "Invalid operation."); } ///  /// Displays exception message. ///  private void DisplayError(Exception ex) { MessageBox.Show(ex.GetType() + "/n/n" + ex.Message + "/n/n" + ex.StackTrace, "Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop); } } // End Form1 ///  /// Handles a thread (unhandled) exception. ///  internal class ThreadExceptionHandler { ///  /// Handles the thread exception. ///  public void Application_ThreadException( object sender, ThreadExceptionEventArgs e) { try { // Exit the program if the user clicks Abort. DialogResult result = ShowThreadExceptionDialog( e.Exception); if (result == DialogResult.Abort) Application.Exit(); } catch { // Fatal error, terminate program try { MessageBox.Show("Fatal Error", "Fatal Error", MessageBoxButtons.OK, MessageBoxIcon.Stop); } finally { Application.Exit(); } } } ///  /// Creates and displays the error message. ///  private DialogResult ShowThreadExceptionDialog(Exception ex) { string errorMessage= "Unhandled Exception:/n/n" + ex.Message + "/n/n" + ex.GetType() + "/n/nStack Trace:/n" + ex.StackTrace; return MessageBox.Show(errorMessage, "Application Error", MessageBoxButtons.AbortRetryIgnore, MessageBoxIcon.Stop); } } // End ThreadExceptionHandler

Visual Basic .NET Implementation

Imports System.Threading Public Class Form1 Inherits System.Windows.Forms.Form Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Subscribe to thread (unhandled) exception events Dim handler As ThreadExceptionHandler = _ New ThreadExceptionHandler() AddHandler Application.ThreadException, _ AddressOf handler.Application_ThreadException End Sub ''' ''' Adds default name to text box. ''' Private Sub btnAdd_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnAdd.Click ' Generate handled exception Add() ' Generate unhandled exception 'AddWithUnhandledException() End Sub ''' ''' Removes name from text box. ''' Private Sub btnRemove_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles btnRemove.Click txtName.Clear() End Sub ''' ''' Adds default name to text box. ''' Throws handled exception. ''' Private Sub Add() Try txtName.Text = "Kevin" Throw New InvalidOperationException( _ "Invalid operation.") Catch ex As System.Exception DisplayError(ex) End Try End Sub ''' ''' Adds default name to text box. ''' Throws unhandled exception. ''' Private Sub AddWithUnhandledException() txtName.Text = "Kevin" Throw New InvalidOperationException( _ "Invalid operation.") End Sub ''' ''' Displays exception message. ''' Private Sub DisplayError(ByVal ex As Exception) MessageBox.Show(ex.GetType().ToString() & _ vbCrLf & vbCrLf & _ ex.Message & vbCrLf & vbCrLf & _ ex.StackTrace, _ "Error", _ MessageBoxButtons.AbortRetryIgnore, _ MessageBoxIcon.Stop) End Sub End Class ' Form1 ''' ''' Handles a thread (unhandled) exception. ''' Friend Class ThreadExceptionHandler ''' ''' Handles the thread exception. ''' Public Sub Application_ThreadException( _ ByVal sender As System.Object, _ ByVal e As ThreadExceptionEventArgs) Try ' Exit the program if the user clicks Abort. Dim result As DialogResult = _ ShowThreadExceptionDialog(e.Exception) If (result = DialogResult.Abort) Then Application.Exit() End If Catch ' Fatal error, terminate program Try MessageBox.Show("Fatal Error", _ "Fatal Error", _ MessageBoxButtons.OK, _ MessageBoxIcon.Stop) Finally Application.Exit() End Try End Try End Sub ''' ''' Creates and displays the error message. ''' Private Function ShowThreadExceptionDialog( _ ByVal ex As Exception) As DialogResult Dim errorMessage As String = _ "Unhandled Exception:" _ & vbCrLf & vbCrLf & _ ex.Message & vbCrLf & vbCrLf & _ ex.GetType().ToString() & vbCrLf & vbCrLf & _ "Stack Trace:" & vbCrLf & _ ex.StackTrace Return MessageBox.Show(errorMessage, _ "Application Error", _ MessageBoxButtons.AbortRetryIgnore, _ MessageBoxIcon.Stop) End Function End Class ' ThreadExceptionHandler

Console and ASP.NET Applications

For Console applications you should use the System.AppDomain.UnhandledException event. To hook it up you would write:

Thread.GetDomain().UnhandledException += new UnhandledExceptionEventHandler(Application_UnhandledException);

And the event handler looks like this.

public static void Application_UnhandledException( object sender, UnhandledExceptionEventArgs e) { // Handle exception.  // The exception object is contained in e.ExceptionObject. }

For ASP.NET applications you use the System.Web.HttpApplication.Error event which is placed in the Global.asax file. This might look something like:

protected void Application_Error(Object sender, EventArgs e) { Exception ex = Server.GetLastError(); // Stop error from displaying on the client browser Context.ClearError(); Response.Write("Application_Error"); Response.Write("Error Message: " + ex.ToString()); }

 

内容概要:本文档是一份关于交换路由配置的学习笔记,系统地介绍了网络设备的远程管理、交换机与路由器的核心配置技术。内容涵盖Telnet、SSH、Console三种远程控制方式的配置方法;详细讲解了VLAN划分原理及Access、Trunk、Hybrid端口的工作机制,以及端口镜像、端口汇聚、端口隔离等交换技术;深入解析了STP、MSTP、RSTP生成树协议的作用与配置步骤;在路由部分,涵盖了IP地址配置、DHCP服务部署(接口池与全局池)、NAT转换(静态与动态)、静态路由、RIP与OSPF动态路由协议的配置,并介绍了策略路由和ACL访问控制列表的应用;最后简要说明了华为防火墙的安全区域划分与基本安全策略配置。; 适合人群:具备一定网络基础知识,从事网络工程、运维或相关技术岗位1-3年的技术人员,以及准备参加HCIA/CCNA等认证考试的学习者。; 使用场景及目标:①掌握企业网络中常见的交换与路由配置技能,提升实际操作能力;②理解VLAN、STP、OSPF、NAT、ACL等核心技术原理并能独立完成中小型网络搭建与调试;③通过命令示例熟悉华为设备CLI配置逻辑,为项目实施和故障排查提供参考。; 阅读建议:此笔记以实用配置为主,建议结合模拟器(如eNSP或Packet Tracer)动手实践每一条命令,对照拓扑理解数据流向,重点关注VLAN间通信、路由选择机制、安全策略控制等关键环节,并注意不同设备型号间的命令差异。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值