处理异常的最佳做法
 

2009-03-25 来源:microsoft

 

计良好的错误处理代码块集可使程序更可靠并且不容易崩溃,因为应用程序可处理这样的错误。下表包含有关处理异常的最佳做法的建议:

  • 知道何时设置 Try/Catch 块。例如,可以以编程方式检查可能发生的条件,而不使用异常处理。在其他情况下,使用异常处理捕捉错误条件是适当的。

    下面的示例使用 if 语句检查连接是否关闭。如果连接未关闭,可以使用此方法而不是引发异常。

    Visual Basic
       If conn.State <> ConnectionState.Closed Then
          conn.Close()
       End If
       if(conn.State != ConnectionState.Closed)
          conn.Close();
    

    在下面的示例中,如果连接未关闭,则引发异常。

    Visual Basic
       Try
          conn.Close()
       Catch ex As InvalidOperationException
          'Do something with the error or ignore it.
       End Try
       try {
         conn.Close();
       }
       catch(InvalidOperationException ex) {
         //Do something with the error or ignore it.
       }
    

    所选择的方法依赖于预计事件发生的频率。如果事件确实是异常的并且是一个错误(如意外的文件尾),则使用异常处理比较好,因为正常情况下执行的代码更少。如果事件是例行发生的,使用编程方法检查错误比较好。在此情况下,如果发生异常,将需要更长的时间处理。

  • 在可潜在生成异常的代码周围使用 Try/Finally 块,并将 Catch 语句集中在一个位置。以这种方式,Try 语句生成异常,Finally 语句关闭或释放资源,而 Catch 语句从中心位置处理异常。
  • 始终按从最特定到最不特定的顺序对 Catch 块中的异常排序。此方法在将特定异常传递给更常规的 Catch 块之前处理该异常。
  • 以“Exception”这个词作为异常类名的结尾。例如:
    Visual Basic
    Public Class EmployeeListNotFoundException
        Inherits Exception
    public class MyFileNotFoundException : Exception {
    }
  • 当创建用户定义的异常时,必须确保异常的元数据对远程执行的代码可用,包括当异常跨应用程序域发生时。例如,假设应用程序域 A 创建应用程序域 B,后者执行引发异常代码。应用程序域 A 若想正确捕捉和处理异常,它必须能够找到包含应用程序域 B 引发的异常的程序集。如果包含应用程序域 B 引发的异常的程序集位于应用程序域 B 的应用程序基下,而不是位于应用程序域 A 的应用程序基下,则应用程序域 A 将无法找到异常,公共语言运行库将引发 FileNotFoundException。为避免此情况,可以两种方式部署包含异常信息的程序集:
    • 将程序集放在两个应用程序域共享的公共应用程序基中

      - 或 -

    • 如果两个应用程序域不共享一个公共应用程序基,则用强名称给包含异常信息的程序集签名并将其部署到全局程序集缓存中。
  • 在 C# 和 C++ 中创建您自己的异常类时,至少使用三个公共构造函数。有关示例,请参见使用用户定义的异常。
  • 在大多数情况下,使用预定义的异常类型。仅为编程方案定义新异常类型。引入新异常类,使程序员能够根据异常类在代码中采取不同的操作。
  • 对于大多数应用程序,从 Exception 类派生自定义异常。最初认为,自定义异常应该从 ApplicationException 类派生;但是在实践中并未发现这样有重大作用。
  • 在每个异常中都包含一个本地化描述字符串。当用户看到错误信息时,该信息从引发的异常的描述字符串派生,而不是从异常类派生。
  • 使用语法上正确的错误信息(包括结束标点符号)。在异常的描述字符串中,每个句子都应以句号结尾。
  • 为编程访问提供 Exception 属性。仅当存在附加信息有用的编程方案时,才在异常中包含附加信息(不包括描述字符串)。
  • 对非常常见的错误情况返回空。例如,如果没找到文件,File.Open 返回空;但如果文件被锁定,则引发异常。
  • 类的设计应使在正常使用中从不引发异常。例如,FileStream 类公开另一种确定是否已到达文件尾的方法。这避免了在读取超过文件尾时引发的异常。下面的示例显示如何读到文件尾。
    Visual Basic
    Class FileRead
       Sub Open()
          Dim stream As FileStream = _
                        File.Open("myfile.txt", FileMode.Open)
            Dim b As Byte
            Dim result As Integer
            ' ReadByte returns -1 at EOF.
            Do 
               result = stream.ReadByte()
               If result = -1 Then Exit Do
                b = CByte(result)
                ' Do something.
            Loop
            ' Call stream.Close() here or in another method.
        End Sub 'Open
    End Class 'FileRead
    class FileRead {
        public void Open() {
            FileStream stream = File.Open("myfile.txt", FileMode.Open);
            byte b;
            int result;
            // ReadByte returns -1 at EOF.
            while ((result = stream.ReadByte()) != -1) {
                b = (byte)result;
                // Do something.
                }
            // Call stream.Close() here or in another method.
        }
    }
    
  • 如果根据对象的当前状态,属性集或方法调用不适当,则引发 InvalidOperationException。
  • 如果传递无效的参数,则引发 ArgumentException 或从 ArgumentException 派生的类。
  • 堆栈跟踪从引发异常的语句开始,到捕捉异常的 Catch 语句结束。当决定在何处放置 Throw 语句时需考虑这一点。
  • 使用异常生成器方法。类从其实现中的不同位置引发同一异常是常见的情况。为避免过多的代码,应使用帮助器方法创建异常并将其返回。例如:
    Visual Basic
    Class File
       Private fileName As String
       
       Public Function Read(bytes As Integer) As Byte()
          If Not ReadFile(handle, bytes) Then
             Throw NewFileIOException()
          End If
       End Function 'Read
       
       Function NewFileIOException() As FileException
          Dim description As String = __unknown ' Build localized string, including fileName.
          Return New FileException(description) '
       End Function 'NewFileIOException
    End Class 'File
    class File {
        string fileName;
        public byte[] Read(int bytes) {
            if (!ReadFile(handle, bytes))
                throw NewFileIOException();
        }
        FileException NewFileIOException() {
            string description = // Build localized string, including fileName.
            return new FileException(description);
         }
    }
    

    或者,使用异常的构造函数生成异常。这更适合全局异常类,如 ArgumentException

  • 引发异常,而不是返回错误代码或 HRESULT。
  • 引发异常时清理中间结果。当异常从方法引发时,调用方应该能够假定没有副作用。

火龙果软件/UML软件工程组织致力于提高您的软件工程实践能力,我们不断地吸取业界的宝贵经验,向您提供经过数百家企业验证的有效的工程技术实践经验,同时关注最新的理论进展,帮助您“领跑您所在行业的软件世界”。
资源网站: UML软件工程组织