首页 > 编程语言 >C#笔记-异常处理

C#笔记-异常处理

时间:2024-03-21 09:33:03浏览次数:23  
标签:C# 捕获 System 笔记 Application ex catch 异常

一、引言

1、什么是异常

  • 异常是程序运行时发生的不正常或错误情况。
  • 异常会打断程序的正常流程,导致程序终止或产生不可预测的结果。

2、为什么要处理异常

  • 提高程序的健壮性和稳定性。
  • 提供友好的错误提示,提升用户体验。
  • 方便开发者定位和修复问题

二、C#中的异常处理机制

1、try…catch语句块

  • try块:包含可能引发异常的代码。
  • catch块:用于捕获并处理特定类型的异常。

2、try…catch…finally

  • finally块:无论是否发生异常都会执行的代码块,常用于资源释放。

3、异常类型

3.1.、System.Exception

是所有异常类的基类,它包含了异常的基本信息,如消息、堆栈跟踪等。

3.2、 System.SystemException

它是系统定义的异常类的基类,通常由系统抛出。

ArgumentException

当向方法的参数传递了一个不合法或不适当的值时引发的异常。

ArgumentNullException

当向方法的参数传递了一个空引用(null)时引发的异常。

ArgumentOutOfRangeException

当向方法的参数传递了一个超出其有效范围的值时引发的异常。

ArithmeticException

在算术运算或类型转换期间发生错误时引发的异常。

IndexOutOfRangeException

当尝试访问数组或集合的无效索引时引发的异常。

InvalidCastException

当尝试执行无效的强制类型转换时引发的异常。

InvalidOperationException

当对象处于无法执行请求的操作的状态时引发的异常。

NullReferenceException

当应用程序尝试在需要对象的情况下使用空引用时引发的异常。

NotSupportedException

当调用某个对象不支持的操作或方法时引发的异常。

FormatException

当字符串的格式不正确时引发的异常。

3.3、 System.ApplicationException

它是用户定义的异常类的基类,通常由应用程序抛出。

三、异常处理的基本用法

1、捕获和处理异常

  • 使用try-catch语句块捕获异常。
  • 在catch块中处理异常,如记录日志、显示错误消息等。
try  
{  
    int result = 10 / 0; // 这里会引发DivideByZeroException异常  
}  
catch (DivideByZeroException ex)  
{  
    Console.WriteLine("除数不能为零: " + ex.Message);  
}

2、使用多个catch块捕获不同异常

  • 可以编写多个catch块来捕获不同类型的异常。
try  
{  
    // 这里可能引发不同类型的异常  
}  
catch (DivideByZeroException ex)  
{  
    Console.WriteLine("除数不能为零: " + ex.Message);  
}  
catch (IndexOutOfRangeException ex)  
{  
    Console.WriteLine("索引越界: " + ex.Message);  
}  
catch (Exception ex)  
{  
    Console.WriteLine("发生未知异常: " + ex.Message);  
}

3、使用finally块释放资源

  • finally块中的代码无论是否发生异常都会执行。
FileStream fs = null;  
try  
{  
    fs = new FileStream("example.txt", FileMode.Open);  
    // 操作文件...  
}  
catch (IOException ex)  
{  
    Console.WriteLine("文件操作异常: " + ex.Message);  
}  
finally  
{  
    if (fs != null)  
    {  
        fs.Close(); // 确保文件流被关闭  
    }  
}

4、throw关键字

throw关键字用于显式地抛出异常。这可以在检测到错误条件时,或者想在方法内部重新抛出捕获的异常时使用。throw关键字后面通常跟着一个异常对象,该对象描述了发生的错误。

4.1、直接抛出异常

当确定某处代码存在问题时,可以直接抛出一个异常。例如:

if (someCondition)  
{  
    throw new InvalidOperationException("Some condition is not met.");  
}

4.2、在catch块中重新抛出异常

有时,可能想捕获一个异常,执行一些清理操作,然后再次抛出该异常。这可以通过在catch块中使用throw;(没有异常对象)来实现,它将重新抛出最近捕获的异常。

try  
{  
    // ... 一些可能会抛出异常的代码 ...  
}  
catch (Exception ex)  
{  
    // 执行一些清理操作  
    // ...  
 
    // 重新抛出异常  
    throw;  
}

4.3、抛出捕获的异常并添加信息

如果想在重新抛出异常时添加更多信息,可以捕获异常,创建一个新的异常对象,并将原始异常作为内部异常(InnerException)传递。

try  
{  
    // ... 一些可能会抛出异常的代码 ...  
}  
catch (Exception ex)  
{  
    // 添加更多信息,并创建一个新的异常对象  
    throw new InvalidOperationException("An error occurred during processing.", ex);  
}

4.4、抛出特定的异常类型

如果知道某个错误条件应该抛出特定类型的异常,可以直接创建并抛出该类型的异常对象。

if (someParameter == null)  
{  
    throw new ArgumentNullException(nameof(someParameter), "The parameter cannot be null.");  
}

4.5、在finally块中抛出异常

虽然不常见,但在某些情况下,可能需要在finally块中抛出异常。但请注意,如果trycatch块中已经抛出了异常,并且finally块中又抛出了新的异常,那么原始异常将被丢失,除非在finally块中显式地保留它。通常最好避免在finally块中抛出异常,因为它可能会导致代码逻辑复杂且难以维护。

5、自定义异常

5.1、创建自定义异常类

  • 继承自Exception或其子类。
  • 可以添加自定义属性或方法。
public class CustomBusinessException : Exception  
{  
    public CustomBusinessException(string message) : base(message)  
    {  
    }  
  
    // 可以添加自定义属性或方法...  
}

5.2、抛出和使用自定义异常

  • 使用throw关键字抛出自定义异常。
  • 在catch块中捕获并处理自定义异常。
try  
{  
    throw new CustomBusinessException("自定义业务异常");  
}  
catch (CustomBusinessException ex)  
{  
    Console.WriteLine("自定义业务异常: " + ex.Message);  
}

四、全局异常捕获

在winform或WPF等程序中,有时会出现程序闪退或者崩溃现象,但又无法定位问题,这个时候可能就需要进行一个全局异常捕获来定位问题。下面介绍几个常用的全局异常捕获方式。

1、在Program.cs里简单粗暴的try…catch

/// <summary>
/// 应用程序的主入口点。
/// </summary>
[STAThread]
static void Main()
{
    try
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new FrmMain());
    }
    catch (Exception ex)
    {
        MessageBox.Show(string.Format("捕获到未处理异常:{0}\r\n异常信息:{1}\r\n异常堆栈:{2}", ex.GetType(), ex.Message, ex.StackTrace));
    }

}

2、更优雅的事件监听:Application类中的ThreadException事件

Application类负责控制整个Windows 程序的运行。Application类的事件ThreadException是在发生未捕获线程异常时发生。

static class Program
{
    /// <summary>
    /// 应用程序的主入口点。
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.ThreadException += Application_ThreadException;
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new FrmMain());
    }
    static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
    {
        Exception ex = e.Exception;
        MessageBox.Show(string.Format("捕获到未处理异常:{0}\r\n异常信息:{1}\r\n异常堆栈:{2}", ex.GetType(), ex.Message, ex.StackTrace));
    }
}

3、子线程异常捕获AppDomain.CurrentDomain.UnhandledException

上面的Application类中的ThreadException事件来捕获异常只能捕获到主线程的异常,如果在我们的程序中使用了多线程技术并且子线程发生了异常,ThreadException事件就无法进行捕获,如果要监听子线程的异常,我们就需要再注册一个事件:AppDomain.CurrentDomain.UnhandledException这个事件是在当前程序域内发生未处理异常时才会发生(如果没有监听Application.ThreadException事件的话,主线程异常最终也会触发这个事件)

static class Program
{
    /// <summary>
    /// 应用程序的主入口点。
    /// </summary>
    [STAThread]
    static void Main()
    {
        Application.ThreadException += Application_ThreadException;
        AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;       
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new FrmMain());
    }
    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Exception ex = e.ExceptionObject as Exception;
        MessageBox.Show(string.Format("捕获到未处理异常:{0}\r\n异常信息:{1}\r\n异常堆栈:{2}\r\nCLR即将退出:{3}", ex.GetType(), ex.Message, ex.StackTrace, e.IsTerminating));
    }
    static void Application_ThreadException(object sender, System.Threading.ThreadExceptionEventArgs e)
    {
        Exception ex = e.Exception;
        MessageBox.Show(string.Format("捕获到未处理异常:{0}\r\n异常信息:{1}\r\n异常堆栈:{2}", ex.GetType(), ex.Message, ex.StackTrace));
    }
}

4、Web程序全局异常捕获

4.1、Global.asax中编写Application_Error函数

在ASP.NET Web Forms应用程序中,全局异常捕获通常通过在Global.asax文件中编写Application_Error事件处理器来实现。Global.asax是一个特殊的ASP.NET文件,它包含应用程序级别和会话级别的事件处理程序,用于响应ASP.NET应用程序或会话级别的事件。

下面是如何在Global.asax中编写Application_Error事件处理器来捕获全局异常的示例:

using System;  
using System.Web;  
using System.Web.Security;  
using System.Web.UI;  
using System.Web.UI.WebControls;  
using System.Web.UI.WebControls.WebParts;  
using System.Web.UI.HtmlControls;  
  
public class Global : System.Web.HttpApplication  
{  
    void Application_Start(object sender, EventArgs e)  
    {  
        // 在应用程序启动时运行的代码  
    }  
  
    void Application_End(object sender, EventArgs e)  
    {  
        // 在应用程序关闭时运行的代码  
    }  
  
    void Application_Error(object sender, EventArgs e)  
    {  
        // 获取当前的HTTP上下文  
        HttpContext context = HttpContext.Current;  
          
        // 如果存在未处理的异常  
        if (context.Error != null)  
        {  
            // 获取异常对象  
            Exception exception = context.Error;  
  
            // 这里可以记录异常到日志系统  
            // 例如:使用NLog, log4net, 或者System.Diagnostics.Trace等  
  
            // 清除错误,以便不会再次触发  
            context.Server.ClearError();  
  
            // 将用户重定向到一个友好的错误页面  
            context.Response.Redirect("~/ErrorPage.aspx", true);  
  
            // 或者你也可以直接将错误信息写入响应中(不推荐在生产环境中使用)  
            // context.Response.Write("An error occurred: " + exception.Message);  
            // context.Response.End();  
        }  
    }  
  
    // 其他事件处理器...  
}

在上面的代码中,Application_Error事件处理器会捕获在ASP.NET应用程序中发生的任何未处理的异常。在事件处理器内部,可以通过HttpContext.Current.Error属性访问异常对象。然后,可以将异常记录到日志系统、清除错误状态以避免重复触发事件、以及将用户重定向到一个友好的错误页面。

需要注意的是,使用Server.ClearError()方法来清除错误是很重要的,这样就不会再次触发Application_Error事件。如果不这样做,可能会导致无限循环或不可预测的行为。

4.2、使用Filter进行全局异常捕获

在ASP.NET Web Forms应用程序中,通常使用Global.asaxApplication_Error事件处理器来进行全局异常捕获。然而,在ASP.NET MVC中,可以使用过滤器(Filter)来实现类似的功能。过滤器允许在请求的不同阶段执行自定义逻辑,包括异常处理。

下面是如何使用过滤器在ASP.NET MVC中进行全局异常捕获的示例:

首先,创建一个实现IExceptionFilter接口的自定义异常过滤器:

using System;  
using System.Web.Mvc;  
public class GlobalExceptionFilter : IExceptionFilter  
{  
    public void OnException(ExceptionContext filterContext)  
    {  
        // 这里可以记录异常到日志系统  
        // 例如:使用NLog, log4net, 或者System.Diagnostics.Trace等  
        // 如果需要,可以修改HTTP响应  
        filterContext.HttpContext.Response.StatusCode = 500; // 内部服务器错误  
        filterContext.ExceptionHandled = true; // 标记异常已被处理  
        // 将用户重定向到一个友好的错误页面,或者返回自定义的错误响应  
        filterContext.Result = new RedirectResult("~/Error/Index"); // 假设有一个ErrorController和Index Action  
        // 或者返回一个ViewResult, JsonResult等,取决于你的需求  
    }  
}

然后,你需要在全局范围内注册这个过滤器。这通常是在Global.asax文件的Application_Start事件或者MvcApplication类中完成的(取决于你的项目结构):

using System.Web.Mvc;  
public class MvcApplication : System.Web.HttpApplication  
{  
    protected void Application_Start()  
    {  
        AreaRegistration.RegisterAllAreas();  
        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);  
        RouteConfig.RegisterRoutes(RouteTable.Routes);  
        // 注册全局异常过滤器  
        GlobalFilters.Filters.Add(new GlobalExceptionFilter());  
    }  
}

或者,如果你有一个单独的FilterConfig类来配置过滤器,你可以在那里添加你的全局异常过滤器:

public class FilterConfig  
{  
    public static void RegisterGlobalFilters(GlobalFilterCollection filters)  
    {  
        filters.Add(new HandleErrorAttribute()); // 默认的异常处理过滤器(可选)  
        filters.Add(new GlobalExceptionFilter()); // 你的自定义全局异常过滤器  
    }  
}

现在,每当MVC应用程序中发生未处理的异常时,GlobalExceptionFilter中的OnException方法就会被调用,我们可以在这里记录异常、修改HTTP响应,或者将用户重定向到一个友好的错误页面。

请注意,ASP.NET MVC还提供了一个内置的HandleErrorAttribute,它可以用来处理控制器或操作中的异常,并将其重定向到一个错误视图。然而,HandleErrorAttribute通常不如自定义的IExceptionFilter灵活,特别是在需要执行复杂的错误处理逻辑时。

最后,对于ASP.NET Web API项目,你可以使用IExceptionHandler接口来实现类似的异常处理逻辑。在Web API中,你通常会创建一个实现IExceptionHandler的类,并在GlobalConfiguration.Configuration.Services.Replace中注册它。

五、注意事项

在Windows程序里,如果同时监听了Application.ThreadException事件和AppDomain.CurrentDomain.UnhandledException事件的话,则异常优先被Application.ThreadException事件捕获。但Application.ThreadException事件只能监听程序主线程抛出的异常。

在Web程序里,如果同时使用了Global.asax中编写Application_Error函数进行全局异常捕获和使用Filter进行全局异常捕获,异常优先被Filter捕获。

标签:C#,捕获,System,笔记,Application,ex,catch,异常
From: https://blog.csdn.net/weixin_63658665/article/details/136896657

相关文章

  • 通过Docker安装MySQL数据库
    1.安装Docker首先,确保你的系统上已经安装了Docker。如果还没有安装,可以访问Docker官网查看安装指南。对于大多数Linux发行版,可以使用以下命令安装Docker:sudoapt-getupdatesudoapt-getinstalldocker.io安装完成后,启动Docker服务并设置开机自启:sudosystemctlstart......
  • 毕业设计——基于facenet实时人脸识别系统的设计与实现+源码+综述
    如需完整源码,可以联系博主获取技术路径:opencv+mtcnn+facenet+python+tensorflow,实现局域网连接手机摄像头,对目标人员进行实时人脸识别一、引言随着信息技术的飞速发展,人脸识别技术已成为身份验证、安全监控等领域的核心技术之一。实时人脸识别系统,以其高效、准确的特点,......
  • 【CKA模拟题】查找集群中使用内存最高的node节点
    题干Forthisquestion,pleasesetthiscontext(Inexam,diffclustername)kubectlconfiguse-contextkubernetes-admin@kubernetesFindtheNodethatconsumesthemostMEMORYinallcluster(currentlywehavesinglecluster).Then,storetheresultin......
  • 备战2024 CKA考试?这些热门考题等你挑战!
    目录温馨提示1、考核知识:基于角色访问控制题目内容官方文档搜索关键字:RBAC做题解答2、考核知识:节点维护指定node节点不可用题目内容官方文档搜索关键字:drain-node做题解答3、考核知识:K8s版本升级题目内容官方参考文档做题解答4、考核......
  • 3/20,switch,后面还送了关机代码
    前面讲过if语句,现在加个switchelse与最近的if进行匹配;可以添加{}来改变选择的if可以反着写常量可以放左边判断时:eg:5==num,这样在输入的时候不会和赋值和判断搞偏switch语句:switch(expression){   caseconstant1: //代码块1     break; #includ......
  • docker 的常见用法
      #docker安装mysqlsudodockerpullmysql:5.7#docker启动mysqlsudodockerrun-p3306:3306--namemysql\-v/mydata/mysql/log:/var/log/mysql\-v/mydata/mysql/data:/var/lib/mysql\-v/mydata/mysql/conf:/etc/mysql\-eMYSQL_ROOT_PASSWORD=root\-dmysql......
  • chrome拓展-chrome插件-强制copy
    方法:浏览器扩展最简单也是最省事的方法,直接安装解除复制限制的扩展完事儿。下面推荐两款扩展:SimpleAllowCopy:Chrome&Edge(Chromium系的应该都可以)AbsoluteEnableRightClick&Copy:Chrome&Edge&Firefox使用方法也很简单,在有复制限制或者右键限制的网站上,点击扩展......
  • C++模板实现之谜:为何只能在头文件中?解密原因与高级分离技术
     概述:C++中模板必须在头文件中实现,因为编译器需要可见的实现以生成模板具体实例的代码。通过头文件,确保模板在每个编译单元中都能被正确展开,提高可维护性。在C++中,模板只能在头文件中实现的主要原因是编译器在使用模板时需要生成对应的代码,而这部分代码必须在编译时可见。以......
  • ACCESS 关于使用VBA选择路径时提示"方法'FileDislog作用于对象'_Application’时失败"
    以下是源码:PrivateSubCommand0_Click()'打开文件选择对话框WithApplication.FileDialog(msoFileDialogFilePicker).AllowMultiSelect=False.Filters.Clear.Filters.Add"Excel文件","*.xls;*.xlsx",1I......
  • Arcade绘制一个笑脸
    """DrawinganexamplehappyfaceIfPythonandArcadeareinstalled,thisexamplecanberunfromthecommandlinewith:python-marcade.examples.happy_face"""importarcade#SetconstantsforthescreensizeSCREEN_WI......