文章目录
前言
异常处理机制为开发者提供了一种有效的方式,能够将程序的控制权从出现问题的部分合理地转移到其他部分,确保程序在遇到意外情况时依然能够保持相对稳定的运行状态,或者至少能够以一种可控的方式终止并给出相应提示。
C# 的异常处理建立在四个至关重要的关键词之上,它们分别是 try、catch、finally 和 throw,各自承担着不同且关键的职责,共同构建起了完善的异常处理体系。]
一、核心关键词解析
(一)try 关键字
try 块就像是一个特殊的 “保护区”,它标识出了一段可能会引发异常的代码块,后续会紧跟一个或多个 catch 块与之配合使用。在这个被 try 包裹的代码区域内执行的语句,就是我们所认为的受保护的代码,一旦其中的语句执行出现异常情况,程序就会立即跳转到对应的 catch 块去尝试进行异常处理。例如:
try
{
// 这里放置可能会引起异常的语句,比如进行一些外部资源读取、复杂运算等操作
int[] array = new int[5];
Console.WriteLine(array[10]); // 此处访问超出数组范围,会引发异常
}
catch( ExceptionName e1 )
{
// 错误处理代码,当上述代码出现对应异常时,会执行此处代码来处理异常情况
}
(二)catch 关键字
程序通过异常处理程序来捕获异常,而 catch 关键字就是用于表示异常捕获的关键标识。开发者可以根据可能出现的不同类型的异常,列出多个 catch 语句来分别捕获它们,以此应对 try 块在不同情况下生成的多种异常情况。例如:
try
{
// 引起异常的语句,比如尝试进行类型不匹配的转换
object obj = "abc";
int num = (int)obj;
}
catch(InvalidCastException e)
{
Console.WriteLine("捕获到类型转换异常: {0}", e.Message);
}
catch(IndexOutOfRangeException e)
{
Console.WriteLine("捕获到数组越界异常: {0}", e.Message);
}
在上述代码中,try 块内的代码可能会因为不同原因引发不同类型的异常,通过设置多个 catch 块,就能够精准地捕获相应异常并执行对应的错误处理代码,从而避免程序因为异常而直接崩溃。
(三)finally 关键字
finally 块有着独特且重要的作用,它用于执行给定的语句,而且无论异常是否被抛出,这些语句都会被执行。这在很多场景中非常关键,比如当我们打开一个文件进行读写操作时,不管在读写过程中是否出现异常,最终都需要确保文件被关闭,以释放相关资源、避免数据丢失或损坏等问题。示例如下:
try
{
FileStream fileStream = new FileStream("test.txt", FileMode.Open);
// 进行文件读取操作,这里的代码可能会出现异常,比如文件格式错误等
// 假设读取文件内容的代码
}
catch(IOException e)
{
Console.WriteLine("文件读取出现异常: {0}", e.Message);
}
finally
{
if (fileStream!= null)
{
fileStream.Close(); // 无论是否出现异常,都要关闭文件流
}
}
(四)throw 关键字
当程序运行过程中发现问题时,就需要通过 throw 关键字来抛出一个异常,以此告知程序的异常处理机制当前出现了特殊情况,需要进行相应处理。例如:
public void SomeMethod(int value)
{
if (value < 0)
{
throw new ArgumentException("输入的值不能为负数");
}
// 正常的业务逻辑代码,当参数符合要求时继续执行
}
在这个方法中,如果传入的参数不符合要求(小于 0),就会抛出一个 ArgumentException 异常,后续如果该方法在 try 块中被调用,就会触发对应的 catch 块来处理这个异常。
二、语法结构与使用方式
假设一个块将出现异常,我们可以在一个方法中使用 try 和 catch 关键字来捕获异常,其语法结构如下所示:
try
{
// 引起异常的语句,这里放置那些可能会出现运行时错误的代码,例如资源访问、运算等操作
}
catch( ExceptionName e1 )
{
// 错误处理代码,针对捕获到的特定类型异常进行相应处理,比如记录日志、提示用户等
}
catch( ExceptionName e2 )
{
// 错误处理代码
}
catch( ExceptionName eN )
{
// 错误处理代码
}
finally
{
// 要执行的语句,通常用于资源清理、收尾工作等,无论是否有异常都会执行
}
通过这样的语法结构,我们可以列出多个 catch 语句来捕获不同类型的异常,这是因为在实际的程序运行中,try 块内的代码可能会由于各种各样的原因在不同情况下生成多种异常,所以需要有针对性地进行处理,确保程序的稳定性和健壮性。
三、C# 中的异常类
在 C# 中,异常是通过类来表示的,并且这些异常类主要是直接或间接地派生于 System.Exception 类,形成了一个层次分明的异常类体系。其中,System.ApplicationException 和 System.SystemException 类是较为重要的两个直接派生自 System.Exception 类的异常类。
(一)System.ApplicationException 类
System.ApplicationException 类主要用于支持由应用程序生成的异常,按照规范,程序员自定义的异常都应该派生自该类。这样做的好处是能够让自定义异常融入到整个 C# 的异常体系中,便于统一管理和处理。例如,当我们开发一个特定业务逻辑的应用,需要针对某些业务规则违背的情况定义异常时,就可以按照如下方式创建自定义异常类:
public class BusinessRuleViolationException : ApplicationException
{
public BusinessRuleViolationException(string message) : base(message)
{
}
}
在上述代码中,BusinessRuleViolationException 类继承自 ApplicationException 类,通过构造函数传递异常消息,以便在抛出该异常时能够清晰地告知出现了何种业务规则违背的情况。
(二)System.SystemException 类
System.SystemException 类则是所有预定义的系统异常的基类,它涵盖了许多在系统运行过程中可能出现的底层异常情况。以下是一些派生自 System.SystemException 类的常见预定义的异常类及其描述:
四、异常处理实例展示
(一)系统异常处理实例
C# 以 try、catch 和 finally 块的形式提供了一种结构化的异常处理方案,通过这种方式能够有效地把核心程序语句与错误处理语句分离开来,使代码结构更加清晰,也便于对异常情况进行针对性处理。以下是一个当除以零时抛出异常的实例:
using System;
namespace ErrorHandlingApplication
{
class DivNumbers
{
int result;
DivNumbers()
{
result = 0;
}
public void division(int num1, int num2)
{
try
{
result = num1 / num2;
}
catch (DivideByZeroException e)
{
Console.WriteLine("Exception caught: {0}", e);
}
finally
{
Console.WriteLine("Result: {0}", result);
}
}
static void Main(string[] args)
{
DivNumbers d = new DivNumbers();
d.division(25, 0);
Console.ReadKey();
}
}
}
当上述代码被编译和执行时,由于在 division 方法中进行除法运算时除数为 0,会抛出 DivideByZeroException 异常,程序会立即跳转到对应的 catch 块进行处理,输出相应的异常捕获信息,然后再执行 finally 块中的代码,输出当前的运算结果(在这个例子中结果为 0),整个过程展示了异常处理机制是如何捕获、处理异常以及执行必要的收尾操作的。
(二)用户自定义异常实例
除了使用系统预定义的异常类来处理常见的异常情况外,开发者还可以根据实际业务需求定义自己的异常。用户自定义的异常类通常是派生自 ApplicationException 类,以下是一个演示如何创建和使用用户自定义异常的实例:
using System;
namespace UserDefinedException
{
class TestTemperature
{
static void Main(string[] args)
{
Temperature temp = new Temperature();
try
{
temp.showTemp();
}
catch (TempIsZeroException e)
{
Console.WriteLine("TempIsZeroException: {0}", e.Message);
}
Console.ReadKey();
}
}
public class TempIsZeroException : ApplicationException
{
public TempIsZeroException(string message) : base(message)
{
}
}
public class Temperature
{
int temperature = 0;
public void showTemp()
{
if (temperature == 0)
{
throw (new TempIsZeroException("Zero Temperature found"));
}
else
{
Console.WriteLine("Temperature: {0}", temperature);
}
}
}
}
在这个实例中,首先定义了一个名为 TempIsZeroException 的自定义异常类,它继承自 ApplicationException 类,用于表示温度为零这种特定的业务异常情况。然后在 Temperature 类的 showTemp 方法中,如果温度值为 0,就会抛出这个自定义异常。在 Main 方法中通过 try-catch 块来捕获并处理这个自定义异常,当代码被编译和执行时,如果温度值确实为 0,就会输出相应的自定义异常提示信息,展示了如何根据业务规则创建和运用自定义异常来增强程序的异常处理能力。
五、抛出对象相关要点
如果异常是直接或间接派生自 System.Exception 类,那么就可以抛出一个对象来表示异常情况。在 catch 块中,开发者还可以使用 throw 语句来再次抛出当前捕获到的对象,将异常继续向上传递,让更外层的异常处理代码有机会进行处理,或者根据具体的业务逻辑和程序架构来决定异常的最终处理方式。例如:
try
{
// 一些可能引发异常的代码
SomeMethodThatMightThrowException();
}
catch(Exception e)
{
Console.WriteLine("捕获到异常: {0}", e.Message);
// 进行一些初步的异常处理,比如记录日志等
LogException(e);
// 再次抛出异常,让上层调用者也能知晓并处理该异常
throw e;
}
在上述代码中,在 catch 块内先对捕获到的异常进行了简单处理(输出信息和记录日志),然后又通过 throw 语句将异常对象重新抛出,这样的操作在复杂的多层调用的程序结构中非常有用,能够确保异常在整个程序的不同层次中都能得到合适的关注和处理,保障程序的健壮性和稳定性。