十、异常
10.1 异常条件
- when子句
- 捕获异常时可以加条件判断是否进行处理
- 例如,catch( ArgumentNullException e) when (tag==true){ . . . }
10.2 重新引发已存在的异常
- 在catch块中调用空throw
- 将捕获的异常再次抛出而不替换栈信息
- 只能在catch块中调用
10.3 引发现有异常而不替换栈信息
- 有些异常在捕获之前已经经过包装
- System.Runtime.ExceptionServices.ExceptionDispatchInfo
Task task = writeWebRequestSizeAsync(url);
try
{
while(!task.Wait(100)){
Console.write(".");
}
}
catch(AggregateException exception)
{
exception = exception.Flattern();
ExceptionDispatchInfo.Capture(exception.innerException).Throw();
}
10.4 常规catch块
- catch{ } 是catch(Exception exception){ }的简写,用于捕获之前未被捕获的异常
- 其他语言的异常不一定都派生自Exception
- 在C# 2.0 之后,所有进入程序集的异常,都会被包装成从Exception派生
- 缺点是没有一个可访问的异常实例
10.5 异常处理规范
- 在大多数情况下,catch块应放在调用栈中较高的位置
- 较低位置可能被重复调用,同一个异常问题被重复多次记录
- 尽量少使用Exception或常规catch块
- 有部分异常是无法通过代码处理从异常恢复
- 例如,OutOfMemoryException,只能通过关闭应用程序解决
10.6 自定义异常
- 必须从Exception或其子类中派生
- 可用innerException属性保存原始异常
- 构造器应包括无参构造、string参数构造以及string+内部异常的构造
class DatabaseException : System.Exceptio
{
public DatabaseException()
{
}
public DatabaseException(string message)
{
InnerException = exception;
}
public DatabaseException(string message, System.Data.SqlClient.SQLException exception)
{
InnerException = exception;
}
}
10.7 可序列化异常
- 可序列化对象
- 可以持久化为一个流,然后根据这个流来重新实例化的对象
- 方式
- 使用System.SerializableAttribute
- 或者实现ISerizable接口
- 必须包含一个构造器来获取System.Runtime.Serialization.SerializationInfo和System.Runtime.Serialization.StreamingContext
10.8 封装异常并重新引发
- 栈中较低位置引发的异常在较高处捕获到时已经没有意义时,可考虑重新引发一个不同的异常
- 例如,地理坐标请求API引发UnauthorizedAccessException,和调用的API完全无关的异常,给调用者造成困惑而不是帮助