C# 异常处理机制详解:从默认捕获到自定义异常实战
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# 编程基础技巧的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









