异常处理

From Apache OpenOffice Wiki
Jump to: navigation, search




UNO 将异常作为一种机制,将错误从调用的方法传播到调用程序。此错误机制优于错误代码(如 MS COM 中所示),可以更好地将错误处理代码与代码逻辑分离开来。此外,Java、C++ 以及其他高级编程语言都提供一种异常处理机制,因此,可以将此机制很方便地映射到这些语言中。


在 IDL 中,异常为数据的结构化容器,与 IDL 结构相当。不能将异常作为返回值或方法参数传送,因为 IDL 编译器不支持这样做。可以在 raise 子句中指定异常,并可以用 any 进行传送。存在两种异常:用户定义的异常运行时异常


用户定义的异常

接口设计者应针对可能发生的每种可能错误情况声明异常。对不同情况声明不同异常,以区分不同的错误情况。

实现可能抛出指定异常以及从指定异常派生的异常。而一定不会抛出未指定的异常,也就是说,如果没有指定异常,则实现一定不会抛出异常。这适用于除后面介绍的 RuntimeExceptions 以外的所有异常。

抛出用户定义的异常时,应使对象保持调用之前所处的状态。如果不能保证这一点,则异常规范必须说明对象的状态。请注意,我们不建议这样做。

每个 UNO IDL 异常必须直接或间接地从 com.sun.star.uno.Exception 派生而来。其 UNO IDL 规范如以下示例所示:

  module com { module sun { module star { module uno { 
  exception Exception
  {
      string Message; 
      com::sun::star::uno::XInterface Context; 
  }; 
  }; }; }; };


该异常有两个成员:

  • 消息应该包含有关错误的详细可读说明(英文),尽管运行时无法评价此说明,但它对于调试非常有用。目前,不存在本地化错误消息的概念。
  • Context 成员应该包含最初抛出异常的对象。


下面的 .IDL 文件片断显示的是一个具有正确异常规范和正确文档的方法。

  module com { module sun { module star { module beans { 
 
  interface XPropertySet: com::sun::star::uno::XInterface
  {
      ...
      /** @returns 
          the value of the property with the specified name. 
 
          @param PropertyName 
              This parameter specifies the name of the property. 
 
          @throws UnknownPropertyException 
              if the property does not exist. 
 
          @throws com::sun::star::uno::lang::WrappedTargetException 
              if the implementation has an internal reason for the
              exception. In this case the original exception
              is wrapped into that WrappedTargetException.
      */
      any getPropertyValue( [in] string PropertyName ) 
          raises( com::sun::star::beans::UnknownPropertyException, 
                  com::sun::star::lang::WrappedTargetException );
      ...
  }; 
 
  }; }; }; };


运行时异常

抛出的运行时异常表示一种异常状态。在 IDL 中,不能在接口方法的 raise 子句中指定运行时异常和从运行时异常派生的异常。

下面是抛出运行时异常的几种原因:

  • 调用时底层进程间桥的连接发生故障。
  • 调用一个已废止的对象(请参阅 com.sun.star.lang.XComponent),且调用的对象由于处于已废止状态而无法实现其规范。
  • 以一种被明确禁止的方式传递某个方法参数。例如,作为方法参数传递空接口引用,而接口规范明确禁止这样传递。

除了 acquire 和 release 以外,每个 UNO 调用都可以抛出 com.sun.star.uno.RuntimeException。这与已成功完成多少调用无关。每个调用程序均应确保自己的对象处于一致状态,即使对另一个对象的调用是通过一个运行时异常来应答的。调用程序还应确保在这些情况下没有发生资源泄漏。例如,已分配的内存、文件描述符等。


如果发生运行时异常,调用程序不知道调用是否已经成功完成 com.sun.star. no.RuntimeException 是从 com.sun.star.uno.Exception 派生的。请注意,在 Java UNO 绑定中,com.sun.star.uno.Exception 是从 java.lang.Exception 派生的,而 com.sun.star.uno.RuntimeException 是从 java。lang.RuntimeException 直接派生的。

对于运行时异常的一种常见错误使用是将其重复用于接口规范期间忘记的异常中。在任何情况下,都应该避免这种现象发生。请考虑定义一个新的接口。

不应该将异常错误地用作一种新的编程流机制。在一个程序会话期间,应该始终可以不抛出任何异常。如果情况不是这样,应该审查接口设计。


好的异常处理

本节提供有关异常处理策略的提示。在某些情况下,下面被称为不好的代码段可能有意义,但通常没有意义。

  • 不要抛出带有空消息的异常

通常情况下,尤其是在一般没有堆栈跟踪的 C++ 代码中,异常中的消息是通知调用程序有关发生异常的原因和起源的唯一方法。消息很重要,当异常来自一个可以抛出各种 UNO 异常的普通接口时,尤其如此。

编写异常时,在其中置入说明性文字。要将文字传送给另一个异常,需要复制文字。

  • 不要捕获不对其进行处理的异常

许多人编写 Helper 函数来简化重复的代码任务。但是,通常按如下所示编写代码:

  // Bad example for exception handling 
  public static void insertIntoCell( XPropertySet xPropertySet ) {
      [...]
      try {
          xPropertySet.setPropertyValue("CharColor",new Integer(0));
      } catch (Exception e) {
      }
  }

此代码无效,因为其中有隐藏的错误。调用程序始终不会知道发生了错误。如果只是编写测试程序或试验 API 的某些方面,这样也无伤大雅(尽管甚至测试程序也应正确编写)。必须处理异常,否则编译器无法正确执行。对于实际应用程序,请处理异常。


正确的解决方案取决于对异常的正确处理。以下是每个程序员至少应该做的事情:

  // During early development phase, this should be at least used instead
  public static void insertIntoCell(XPropertySet xPropertySet) {
      [...]
      try {
          xPropertySet.setPropertyValue("CharColor",new Integer(0));
      } catch (Exception e) {
          e.dumpStackTrace();
      }
  }

上面的代码转储异常及其堆栈跟踪,因此,stderr 会收到有关异常发生的消息。在开发阶段这是可以接受的,但对于已部署的代码,这样做是不够的。客户不会观察 stderr 窗口。


必须确定可以处理的错误级别。有时,最好不要在本地捕获异常,而是扩展异常链。然后可以通过对话框通知用户发生的错误。请注意,您甚至可以在 main() 函数上指定异常:

  // this is how the final solution could look like
  public static void insertIntoCell(XPropertySet xPropertySet) throws UnknownPropertyException,
          PropertyVetoException, IllegalArgumentException, WrappedTargetException {
      [...]
      xPropertySet.setPropertyValue("CharColor",new Integer(0));
  }


一般情况下,如果无法从 Helper 函数的异常中恢复,则会让调用程序确定结果。请注意,您甚至可以在 main() 方法上抛出异常。


Content on this page is licensed under the Public Documentation License (PDL).
Personal tools
In other languages