C# 中的异常层次结构设计






1.64/5 (8投票s)
2006 年 10 月 9 日
5分钟阅读

50821
如何在 C# 中设计异常层次结构
引言
在本文中,我将讨论 C# 中的异常处理策略。
基本上有 4 种错误处理策略
- 每个函数都返回一个状态,如 HRESULT / bool。
- 设置一个全局变量/结构来存储错误信息
- Set Jump 和 Long Jump
- 异常处理
没有一种最佳策略可以满足所有产品需求。在某些情况下,我们需要混合使用这些策略,因为单一策略并不适用。在本文中,我将只讨论如何有效地使用异常策略。
假设:开发人员对 .NET 中的异常处理类有非常深入的了解。
一些要点
1. 异常不一定就是错误。异常是否代表错误取决于发生异常的应用程序。在一种情况下,当找不到文件时抛出的异常可能被视为错误,但在另一种情况下则可能不是。
2. 所有异常类都应派生自 Exception 基类
3. ApplicationException 作为所有应用程序特定异常类的基类
4. 最重要的一点是:避免不必要地抛出异常。
请参考本文开头的异常类层次结构。
此层次结构使您的应用程序能够受益于以下方面:
1. 更轻松的开发,因为您可以在产品基类应用程序异常类中定义属性和方法,您的其他应用程序异常类可以继承这些属性和方法。
2. 在应用程序部署后创建的新异常类可以派生自层次结构中现有的异常类型。现有的异常处理代码,如果捕获的是新异常对象的基类,则可以在不修改现有代码的情况下捕获新异常,并将其视为对象之一的基类。
在创建层次结构时,请使用以下问题来帮助您决定是否需要创建新的异常类:
- 是否存在针对此条件的异常?
- 某个特定的异常是否需要离散处理?
- 您是否需要针对某个特定异常进行特定的行为或获取额外信息?
设计您的产品基类异常
/// <summary>
/// Facility 指定错误发生的位置。保留用于 COM 错误
/// </summary>
public enum Facility
{
Feature1,
Feature2
}
/// <summary>
/// 此类定义了产品中所有自定义异常的基础类型。所有场景相关的异常都应派生自 BaseException 类
/// </summary>
public class BaseException : ApplicationException
{
public BaseException(Facility facility, short errorNo) :base()
{
HResult = MakeHResult(facility,errorNo);
}
public BaseException(Facility facility, short errorNo,string message) : base(message)
{
HResult = MakeHResult(facility,errorNo);
}
public BaseException(Facility facility, short errorNo, string message, Exception inner) : base(message, inner)
{
HResult = MakeHResult(facility,errorNo);
}
public BaseException(Facility facility, short errorNo,string message, string context) : base(message)
{
HResult = MakeHResult(facility,errorNo);
this.context = context;
}
public BaseException(Facility facility, short errorNo, string message, string context, Exception inner) : base(message, inner)
{
HResult = MakeHResult(facility,errorNo);
this.context = context;
}
public int ErrorCode
{
get
{
return this.HResult;
}
set
{
this.HResult = value;
}
}
/// <summary>
/// 一个暴露 ErrorContext 的属性。Context 的内容
/// 将根据异常的类型和异常的上下文而有所不同
/// 例如,如果异常与文件有关,它可能包含出错文件的名称,
/// 开发人员应提供足够的数据来详细识别错误上下文。
/// </summary>
public string ContextData
{
get
{
return this.context;
}
set
{
this.context = value;
}
}
/// <summary>
/// 根据严重性、设施和错误编号的值返回 HRESULT
/// </summary>
/// <param name="severity">要使用的严重性</param>
/// <param name="facility">要使用的设施</param>
/// <param name="errorNo">错误编号 </param>
/// <returns>根据上述 3 个值构造的 HRESULT</returns>
protected int MakeHResult(Facility facility, short errorNo)
{
// 创建 HR
int result = (int)1 << 31;
result += (int) facility << 16;
result += errorNo;
return result;
}
private string context;
}
为什么要使用错误编号?
考虑两种错误状态:FileNotFoundException 和 InvalidFilePathException。与其创建两个封装相同数据的异常类,不如编写一个枚举,然后像这样传递场景:
public enum ErrorScenario
{
FileNotFound,
InvalidFilePath
}
关于如何在 C# 中处理 COM 错误还有很多内容。下表将 HRESULT 映射到 .NET 中与之对应的异常类。
HRESULT |
. |
|
ApplicationException |
|
ArgumentException |
|
ArgumentOutOfRangeException |
|
ArithmeticException |
|
ArrayTypeMismatchException |
|
DivideByZeroException |
|
FileNotFoundException |
|
FormatException |
|
IndexOutOfRangeException |
|
InvalidCastException |
|
MulticastNotSupportedException |
|
NotFiniteNumberException |
E_NOTIMPL |
NotImplementedException |
|
NotSupportedException |
|
NullReferenceException |
E_OUTOFMEMORY |
OutOfMemoryException |
|
OverflowException |
|
PathTooLongException |
|
SecurityException |
有关更多详细信息,您可以联系我:SumitkJain@hotmail.com