异常的最佳做法
设计良好的应用处理异常和错误以防止应用崩溃。 本文描述处理和创建异常的最佳做法。
以下列表包含创建自己的异常和引发异常时应遵循的准则。
-
类的设计应使在正常使用中从不引发异常。 例如,FileStream 类提供可帮助确实是否已到达文件末尾的方法。 这避免了在读取超过文件尾时引发的异常。 下面的示例显示如何读到文件尾。
class FileRead { public void ReadAll(FileStream fileToRead) { // This if statement is optional // as it is very unlikely that // the stream would ever be null. if (fileToRead == null) { throw new System.ArgumentNullException(); } int b; // Set the stream position to the beginning of the file. fileToRead.Seek(0, SeekOrigin.Begin); // Read each byte to the end of the file. for (int i = 0; i < fileToRead.Length; i++) { b = fileToRead.ReadByte(); Console.Write(b.ToString()); // Or do something else with the byte. } } }
-
引发异常,而不是返回错误代码或 HRESULT。
-
对于极其常见的错误案例,返回 null 而不是引发异常。 极其常见的错误案例可被视为常规控制流。 通过在这些情况下返回 null,可最大程度地减小对应用的性能产生的影响。
-
在大多数情况下,使用预定义的异常类型。 引入新异常类,使程序员能够根据异常类在代码中采取不同的操作。
-
如果根据对象的当前状态,属性集或方法调用不适当,则会引发 InvalidOperationException 异常。
-
如果传递的参数无效,则会引发 ArgumentException 异常或派生自 ArgumentException 的类。
-
对于大多数应用,从 Exception 类派生自定义异常。从 ApplicationException 类派生并没有很大意义。
-
以“Exception”一词作为异常类名的结尾。 例如:
-
在 C# 和 C++ 中,创建自己的异常类别时至少使用三种公共构造函数:默认构造函数、采用字符串消息的构造函数和采用字符串消息和内部异常的构造函数。 有关示例,请参见如何:创建用户定义的异常。
-
Exception() ,它使用默认值。
-
Exception(String) ,它接受字符串消息。
-
Exception(String, Exception) ,它接受字符串消息和内部异常。
-
-
当创建用户定义的异常时,必须确保异常的元数据对远程执行的代码可用,包括当异常跨应用域发生时。 例如,假设应用域 A 创建应用域 B,后者执行引发异常的代码。 应用域 A 若想正确捕获和处理异常,它必须能够找到包含应用域 B 所引发的异常的程序集。 如果包含应用域 B 引发的异常的程序集位于应用域 B 的应用程序基目录下,而不是位于应用域 A 的应用程序基目录下,则应用域 A 将无法找到异常,并且公共语言运行时将引发 FileNotFoundException 异常。 为避免此情况,可以两种方式部署包含异常信息的程序集:
-
将程序集放在两个应用域共享的公共应用程序基中。
- 或 -
-
如果两个应用域不共享一个公共应用程序基,则用强名称为包含异常信息的程序集签名并将其部署到全局程序集缓存中。
-
-
在每个异常中都包含一个本地化描述字符串。 用户看到的错误消息派生自引发的异常的描述字符串,而不是派生自异常类。
-
通过编程方式使用正确的错误消息(包括结束标点)。 在异常的描述字符串中,每个句子都应以句号结尾。 例如,“记录表已溢出。”将是正确的描述字符串。
-
为编程访问提供 Exception 属性。 仅当存在附加信息有用的编程方案时,才在异常中包含附加信息(不包括描述字符串)。
-
堆栈跟踪从引发异常的语句开始,到捕获异常的 catch 语句结束。 当决定在何处放置 throw 语句时需考虑这一点。
-
使用异常生成器方法。 类从其实现中的不同位置引发同一异常是常见的情况。 为避免过多的代码,应使用帮助器方法创建异常并将其返回。 例如:
class FileReader { private string fileName; public FileReader(string path) { fileName = path; } public byte[] Read(int bytes) { byte[] results = FileUtils.ReadFromFile(fileName, bytes); if (results == null) { throw NewFileIOException(); } return results; } FileReaderException NewFileIOException() { string description = "My NewFileIOException Description"; return new FileReaderException(description); } }
或者,使用异常的构造函数生成异常。 这更适合全局异常类,例如 ArgumentException。
-
引发异常时清理中间结果。 当异常从方法引发时,调用方应能够假定没有副作用。
- 来源:http://msdn.microsoft.com/zh-cn/library/seyhszts.aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-1