【问题标题】:Should managed code return an error or throw exceptions to unmanaged code?托管代码应该返回错误还是向非托管代码抛出异常?
【发布时间】:2010-10-24 10:13:57
【问题描述】:

我即将向使用 COM 的旧版 C++ 应用程序公开一个用 C# 编写的服务。向非托管客户端报告错误的最佳方法是什么?抛出异常还是简单地返回错误值?

谢谢, 斯特凡诺

【问题讨论】:

    标签: c# c++ exception com


    【解决方案1】:

    你应该抛出异常。异常由框架映射到 HRESULTS,HRESULT 是向 COM 客户端返回错误的标准方式,所以这是要走的路。

    每个异常类型都有一个 HResult 属性。当从 COM 客户端调用的托管代码引发异常时,运行时会将 HResult 传递给 COM 客户端。如果您需要特定于应用程序的 HRESULT 代码,您可以创建自己的自定义异常类型并设置 Exception.HResult 属性。

    需要注意的一点是,当向 COM 客户端抛出异常时,调用堆栈信息将丢失。因此,在传播到 COM 客户端之前记录异常可能是个好主意。

    我有时使用的一种技术如下:为记录和重新抛出异常的 COM 客户端显式实现 ComVisible 接口。 COM 客户端使用 ComVisible 接口在传播异常之前记录它们。 .NET 客户端使用具体类,并希望自己安排异常处理。写起来有点冗长,但在您随后进行故障排除时会有所帮助。

    这种方法的另一个优点是您可以为 COM 客户端定制一个针对 COM 限制的 API,以及为标准 .NET 客户端定制一个更标准的 API。例如,COM 客户端仅限于通过引用传递数组,而 .NET 客户端不鼓励通过引用传递。

    例子:

    [
    ComVisible(true),
    GuidAttribute("..."),
    Description("...")
    ]
    public interface IMyComVisibleClass
    {
        // Text from the Description attribute will be exported to the COM type library.
    
        [Description("...")]
        MyResult MyMethod(...);
    
        [Description("...")]
        MyOtherResult MyArrayMethod([In] ref int[] ids,...);
    }
    ...
    [
    ComVisible(true),
    GuidAttribute("..."),
    ProgId("..."),
    ClassInterface(ClassInterfaceType.None),
    Description("...")
    ]
    public class MyComVisibleClass : IMyComVisibleClass
    {
        public MyResult MyMethod(...)
        {
            ... implementation without exception handling ...
        }
    
        public MyOtherResult MyArrayMethod(int[] ids,...)
        {
            ... input parameter does not use ref keyword for .NET clients ...
            ... implementation without exception handling ...
        }
    
        MyResult IMyComVisibleClass.MyMethod(...)
        {
            // intended for COM clients only
            try
            {
                return this.MyMethod(...);
            }
            catch(Exception ex)
            {
                ... log exception ...
                throw;   // Optionally wrap in a custom exception type
            }
        }
    
        MyOtherResult IMyComVisibleClass.MyArrayMethod(ref int[] ids, ...)
        {
            // intended for COM clients only
            try
            {
                // Array is passed without ref keyword
                return this.MyArrayMethod(ids, ...);
            }
            catch(Exception ex)
            {
                ... log exception ...
                throw;   // Optionally wrap in a custom exception type
            }
        }
    
    }
    

    【讨论】:

      【解决方案2】:

      我同意其他人的观点,即在不熟悉您的项目的情况下,这不是“是或否”的答案。

      这取决于许多因素,例如:

      • 安全性(即您的客户应该了解您的异常情况)
      • 效率(即处理时间至关重要)
      • 可维护性(即您能否更改旧版 C++ 代码以解析异常条件)

      Here's a good blog post that discusses a number of subtle points about exception processing.

      作者推荐两种方法之一:

      要么:

      • 返回错误文档。

      或:

      • 记录所有关于 服务器上的异常。
      • 创建一个新的 引用记录的异常 信息。
      • 将新异常发送到 用于客户端处理的客户端 和报告。

      就个人而言,我认为您应该避免将 C# 服务与 C++ 应用程序紧密耦合。换句话说,编写您的 C# 服务,以便理论上任何消费者都可以使用它。同样,您的 C++ 代码应编写为不依赖于 C# 服务的内部工作,因此对异常(或错误代码)的更改或添加不会破坏使用者。

      【讨论】:

        【解决方案3】:

        如果您的 COM 应用程序在调用 C# 服务时支持 IErrorInfo 接口,并且它是一个完全内部的项目,那么抛出异常可能是最好的选择,因为它可以捕获最多的信息。然而,COM 传统上依赖 HR 结果来传达状态结果,如果要将服务发布到其他来源,可能会更好。

        编辑:我更喜欢乔的回答。

        【讨论】:

        • 异常被映射到 HRESULTS,所以这是最好的方法。请参阅下面的回复。
        【解决方案4】:

        我认为这取决于遗留应用程序将如何反应。如果它理解错误返回值,那么就采用这种方法。如果没有,那么您将不得不抛出异常并希望它能够适当地处理它们。

        另外,如果它是错误的引用(例如空引用)或其他严重错误,我总是会抛出异常。但是,对于消费者无法事先检查的事情,应避免异常(例如,空的搜索不应引发异常)。

        【讨论】:

          【解决方案5】:

          最好的判断是你是使用异常还是返回值。 “抛出异常”在几个方面胜过“返回值”。但在某些情况下,返回值就足够了。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2011-01-21
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-07-03
            相关资源
            最近更新 更多