首页 > 解决方案 > 异常中的错误代码

问题描述

我有一个专有的 C# 库,它基本上是一些 C++ 代码的包装器。它提供的功能如下所示:

public int func(in params, out params...)

int 返回信号成功(0),一些错误代码(<0)或一些数据(>0,例如成功读取的字节数)。此外,该库提供了一个public EnumErrorCode GetLastError()功能

我的任务是编写另一个包装器,它将带有错误代码的每个函数转换为抛出异常的函数

虽然很容易将返回码放入异常中

public class MyException : Exception
{
    public int RetVal { get; }
    public MyException(int retVal) : { RetVal = retVal;}
    public MyException(string message, int retVal) : base(message) { RetVal = retVal; }
}

像这样使用

public void func(int a)
{
    var retVal = api.func(a);
    switch (retVal)
    {
        case 0: return;
        case -1: throw new MyException("same error across all funcs", retVal);
        case -2: throw new MyException("func specific error", retVal);
        default: throw new MyException(retVal);
    }
}

我很难找到正确的方法来让 EnumErrorCode GetLastError() 进入异常 首先,我应该引入一个新属性public EnumErrorCode ErrorCode { get; },在专有 C# 库中定义 EnumErrorCode 还是应该只存储一个 int?GetLastError()其次,抛出异常时可以调用吗?

case -1: throw new MyException("Mayhem!", retVal, GetLastError());

在抛出时再次调用外部未知代码似乎是不对的。还有哪些其他可能性?


感谢 Fildor 的详细回答。我想我会混入那个函数特定的消息(由 API 文档提供),并在 Create 函数中使用字典来映射 EnumErrorCode

public interface IMyExceptionFactory
{
    MyException Create(string funcSpecificCodeMsg, int returnCode, int errorCode);
}

// snip
int returnCode = CallToThirdParty();
if (returnCode < 0)
{
    int errorCode = -1;
    try
    {
        errorCode = (int)GetLastError();
    }
    catch (Exception) { }
    switch (returnCode)
    {
        case -1: throw _exceptionFactory.Create("invalid handle", returnCode, errorCode);
        case -2: throw _exceptionFactory.Create("func specific error -2", returnCode, errorCode);
            ...
        default: throw _exceptionFactory.Create("Unknown returnCode", returnCode, errorCode);
    }
    
}

标签: c#exception

解决方案


第一:重申评论中的观点:

  • 如果您需要它是线程安全的,您需要对您的客户进行两次“原子”调用。
  • 为了安全起见,我会附上GetLastError另一个尝试/捕获。

使用第 3 方枚举

这取决于您想对适配器的客户端隐藏第 3 方的程度。如果他们不应该知道(至少在使用您的适配器时,当然,他们可能知道有一个 3rd 方)第 3 方,那么您需要隐藏原始 Enum。

这可以通过制作您自己的该枚举的“副本”来完成。这是可能的,但它有严重的缺陷:在第 3 方的每次更新中,您都需要检查枚举是否已更改并相应地更改您的代码。这甚至可能导致重大变化。

我过去所做的只是使用和公开整数值并提供一个ErrorCodeToString函数,该函数基本上将 int 转换为 3rd Party 枚举,然后执行ToString. 这对我的目的来说已经足够了,但我当然不认为这是甚至是最佳实践”。

创建例外

回顾一下我们所拥有的:

  • 函数可以有返回码
    • 0 ==“好的”
    • >0(只有其中一些)== 一些整数值的结果。
    • <0 , 其中...
      • '-1' 是所有函数共享的常见错误
      • '-2' 和更小是特定于函数的错误(每个函数的含义不同)
  • 在负返回代码之上,可以通过调用来检索错误代码(=> 枚举)GetLastError

现在,一个问题是:除了拥有 ErrorCodeEnum 值之外,维护一个包含所有特定函数返回码的详尽表格是否有益?

如果是:事情变得有点复杂。但也许我们现在可以把它放在一边专注于 Enum 值。

当然,您不希望为每个函数编写自定义处理。所以,你想要的是某种异常工厂。

我对此的假设是:

  • 您可能希望为每个枚举值分配不同的异常消息。
  • 您已经展示了一个自定义MyException类型,因此您可能想要使用它。

现在,您需要做出设计决定。您可以使用一种异常类型并使其具有属性。或者,您可以从基本异常类型派生“子”类型。

后者可以方便地将您想要/需要处理的异常与您(或客户)想要“冒泡”的其他异常进行分组或挑选出来。

无论哪种方式,您都希望有一个类似的界面

public interface IMyExceptionFactory
{
    MyException Create(int returnCode, int errorCode);
}

在你的适配器中使用(我想你会想要注入它),所以它可以发挥它的魔力,你可以扔掉它给你的任何东西。

例如:

// snip
int returnCode = CallToThirdParty();
if( returnCode < 0 )
{
    int errorCode = GetLastError();
    throw _exceptionFactory.Create(returnCode, errorCode);
}

在其实现中,您将使用 Message(可能是派生类型)和设置为正确值的其他属性构建适当的异常。

例如,您可以构建一个字典,让您通过 errorCodes 等查找消息。然后您可以决定将其构建为硬编码或通过配置...


推荐阅读