【问题标题】:Errorcodes in exception异常中的错误代码
【发布时间】:2021-08-31 06:52:24
【问题描述】:

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

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

返回的 int 表示成功(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);
    }
    
}

【问题讨论】:

  • 如果您需要它是线程安全的,您需要对您的客户进行“原子”调用。另外,为了安全起见,我会在 GetLastError 中加上另一个 try/catch。
  • 关于 Enum 的事情:你需要/想要完全隐藏第 3 方吗?然后不要使用 Enum 而只是使用它的 int 表示。请注意这对您的客户意味着什么。如果您想要一个更“可读”的值的便利,您可以创建一个函数,返回枚举值的“ToString”(例如,当然还有更多选项)。
  • 所有第 3 方 API 的错误代码是否一致?或者错误代码是特定于函数的(= 每个函数都有自己的一组错误代码,在不同的函数上,相同的代码可以有不同的语义)?如果只有一组错误代码,我会将其排除并创建一个异常工厂。
  • 最后:请注意,在您的实施中,您会忽略积极的结果。这将默认抛出MyException(retVal)。好像不太对?
  • 如果本机库为错误代码声明了一个枚举,那么您对错误原因了解很多。因此,您只需要 MyException(int) 构造函数,并且可以将错误代码转换为构造函数内的描述性字符串。最后一条语句就好了,GetLastError 被调用之前抛出异常,越早越好。

标签: c# exception


【解决方案1】:

首先:重申一下 cmets 的观点:

  • 如果您需要它是线程安全的,您需要对您的客户端进行“原子”调用。
  • 为了安全起见,我会在 GetLastError 后面加上另一个 try/catch。

使用第 3 方枚举

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

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

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

创建异常

回顾一下我们所拥有的:

  • 函数可以有返回码
    • 0 == “好的”
    • >0(仅其中一些)== 一些整数值结果。
    • “-1”是所有函数共有的常见错误
    • '-2' 和更小是特定于函数的错误(每个函数的含义不同)
  • 除了负返回代码之外,还可以通过调用 GetLastError 来检索错误代码 (=> 枚举)。
  • 现在,一个问题是:除了只有 ErrorCodeEnum 值之外,维护一个包含所有特定函数返回码的详尽表格是否有益?

    如果是:事情变得有点复杂。但也许我们可以将其暂时放在一边,专注于 Enum 值。

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

    我对此的假设是:

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

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

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

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

    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(可能是派生类型)和设置为正确值的其他属性构建适当的异常。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-04-04
      • 2012-12-07
      • 2018-05-23
      • 1970-01-01
      • 2017-05-19
      • 1970-01-01
      • 1970-01-01
      • 2010-11-26
      相关资源
      最近更新 更多