C# 异常处理机制详解:从默认捕获到自定义异常实战

半杯拿铁
半杯拿铁
发布时间:2026-10-16 | 4人浏览过
原创

C# 中的异常处理机制用于发现和处理运行时的错误。了解如何正确使用 try、catch 和 finally 块,以及何时抛出或捕获异常,是编写健壮 .NET 应用程序的关键。

.NET 默认异常处理机制

当开发人员未提供特定的异常处理逻辑时,.NET 框架会采用其默认的机制来应对运行时错误。若错误未被局部捕获,程序可能会终止或显示系统级的错误弹窗。

以下代码展示了未进行异常处理时,执行除以零操作引发的后果:

class Program
{
    static void Main(string[] args)
    {
        int a = 0;
        int result = 100 / a;
        Console.WriteLine(result);
        Console.ReadKey();
    }
}

上述代码在执行时会直接触发未处理的异常,导致程序崩溃或非预期行为。

使用 try...catch 手动捕获异常

为了控制程序的流,可以使用 try 块包裹可能出错的代码,并在 catch 块中处理特定的异常类型。这种方法允许程序在错误发生时继续运行或给出友好提示。

例如,专门捕获除数为零的异常 DivideByZeroException:

class Program
{
    static void Main(string[] args)
    {
        int a = 0;
        int result = 0;
        try
        {
            result = 100 / a;
            Console.WriteLine("这里不会执行");
        }
        catch (DivideByZeroException exception)
        {
            Console.WriteLine("出现异常");
        }
        Console.WriteLine(result);
        Console.ReadKey();
    }
}

在此例中,由于发生了 DivideByZeroException,控制权转移到对应的 catch 块,后续的打印语句不会执行,程序正常结束。

第 1 步:添加清理逻辑(finally 块)

finally 块通常用于资源清理,无论是否发生异常,该块内的代码都一定会执行。这是确保文件句柄关闭、数据库连接释放等操作的安全方式。

结合 finally 的完整结构如下:

class Program
{
    static void Main(string[] args)
    {
        int a = 0;
        int result = 0;
        try
        {
            result = 100 / a;
            Console.WriteLine("这里不会执行");
        }
        catch (DivideByZeroException exception)
        {
            Console.WriteLine("出现异常");
        }
        finally
        {
            Console.WriteLine("放行吧,肯定会执行到我这里的~~");
        }
        Console.WriteLine(result);
        Console.ReadKey();
    }
}

运行结果表明,尽管发生了异常,finally 块依然被执行,保证了后续流程的完整性。

第 2 步:多层级异常捕获

一个 try 块后面可以跟随多个 catch 块,以便针对不同种类的异常采取不同的处理策略。需要注意的是,一旦某个 catch 块匹配并捕获了异常,随后的其他 catch 块将不再执行。

最佳实践是将具体的异常类型放在前面,通用的 Exception 基类放在最后:

class Program
{
    static void Main(string[] args)
    {
        int a = 0;
        int result = 0;
        try
        {
            result = 100 / a;
            Console.WriteLine("这里不会执行");
        }
        catch (DivideByZeroException exception)
        {
            Console.WriteLine("不能被0除的异常");
        }
        catch (Exception ex)
        {
            Console.WriteLine("其他异常");
        }
        finally
        {
            Console.WriteLine("放行吧,肯定会执行到我这里的~~");
        }
        Console.WriteLine(result);
        Console.ReadKey();
    }
}

第 3 步:捕获所有异常

如果不需要获取异常对象的详细信息,可以使用不带参数的 catch 块来捕获任何类型的异常。但这会降低调试的精确度,通常用于顶层的全局异常记录。

try
{
    // 潜在错误代码
}
catch
{
    Console.WriteLine("发生了未知异常");
}

主动抛出与层级捕获

除了被动捕获由系统产生的异常,开发人员也可以使用 throw 关键字主动抛出异常,以指示业务逻辑中的非法状态。

以下代码演示了主动抛出一个标准的 DivideByZeroException:

class Program
{
    static void Main(string[] args)
    {
        try
        {
            throw new DivideByZeroException("除数不能为零");
        }
        catch (DivideByZeroException e)
        {
            Console.WriteLine("异常被捕获");
        }
        Console.WriteLine("最后想说的");
        Console.ReadKey();
    }
}

在实际开发中,往往存在方法的调用层级。建议在较低层级的方法中检测错误并抛出异常,而在较高层级的上下文(如 UI 层或入口函数)中进行统一的捕获和处理。

示例:在辅助类 Calculate 内部捕获并重新抛出异常,由主程序接收:

class Program
{
    static void Main(string[] args)
    {
        Calculate c = new Calculate();
        try
        {
            c.Divide();
        }
        catch (Exception e)
        {
            Console.WriteLine("在高层级捕获异常");
        }
        Console.WriteLine("最后想说的");
        Console.ReadKey();
    }
}
public class Calculate
{
    public void Divide()
    {
        try
        {
            int a = 0;
            int result = 100 / a;
        }
        catch (DivideByZeroException e)
        {
            // 选择重新抛出,不在此处消费异常
            throw;
        }
    }
}

这种模式使得低层组件保持简洁,而高层组件拥有完整的上下文信息来处理用户反馈。

自定义异常类

为了满足特定业务的验证需求,可以创建继承自 System.Exception 的自定义异常类。这有助于区分不同类型的业务错误。

class Program
{
    static void Main(string[] args)
    {
        try
        {
            throw new MyException("i am custom exception");
        }
        catch (Exception e)
        {
            Console.WriteLine("捕获到自定义异常了~~");
        }
        Console.WriteLine("最后想说的");
        Console.ReadKey();
    }
}
// 定义自定义异常类
public class MyException : Exception
{
    public MyException(string str)
    {
        Console.WriteLine("这是我自定义的异常:" + str);
    }
}

通过继承 Exception,MyException 可以被标准的异常捕获机制识别。在构造函数中可以初始化消息或其他元数据。

最佳实践总结

  • 灵活性:.NET 的异常处理并不局限于单一的固定结构,可以根据需要组合 try、catch 和 finally。
  • 职责分离:尽量在逻辑较低的层次(如类库或工具方法)抛出异常,指明问题所在;在较高的层次(如控制器或服务层)捕获异常,决定如何响应用户。
  • 具体优先:先捕获具体的异常类型,最后再使用基类 Exception 兜底,避免掩盖其他未知的严重错误。

以上就是 C# 各种异常处理方式总结的详细内容,更多关于 C# 编程基础技巧的资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表