异常命名约定和范围

Exception naming conventions and scope

我们有多个启用了可空引用类型的 .NET 6 项目。到目前为止,我们懒得为特定错误分配或创建异常类型。

目前我们代码中最常用的异常类型是ArgumentException和ArgumentNullException。

后者的用例主要是:
在完整属性中:(我认为这些都很好)

public string OfferId 
{ 
    get => _offerId ?? throw new ArgumentNullException($"{nameof(ItemDTO)}.{nameof(OfferId)}");
    set => _offerId = value;
}

在构造函数中:(这些也很好)

NativeProductId = rawProduct.NativeProductId ??
            throw new ArgumentNullException($"{nameof(rawProduct)}.{nameof(rawProduct.NativeProductId)}");

然后我们将它们用于任何远程闻到 null 的东西,如果 HttpRequest returns 空内容,如果 JsonConvert returns null,则使用它们,等等(这显然是不好的)

现在我们要重构的时候,很难理解在处理不同的错误时,具体的异常应该是怎样的。

(我在谷歌搜索时发现了很少的相关信息)。

即自定义异常 'DataNotFoundException' 似乎非常广泛,但可能可以描述这两种情况(空内容和反序列化)。

另一方面,'ResposeContentNullException' 和 'JsonDeserializationNullException' 非常具体,但这意味着必须创建很多它们才能涵盖所有情况。

stack-overflow 的好人在创建、命名和使用异常时是否有任何规则或不成文的约定?

异常可以看作是“一种特殊的return值”。它们是您的方法合同的一部分。如果您的方法已记录在案,则该文档应包括可以抛出的异常列表以及在何种情况下抛出异常(参见基础 class 库中的 this example)。因此,在我看来,例外应该是 designed/named 并考虑到您的方法的 caller

换句话说,ResposeContentNullExceptionJsonDeserializationNullException 太具体了,因为它们包含 关于您的方法实施细节的信息 --- 细节你方法的调用者不需要关心。

而是查看文档,找出在什么情况下响应内容可以为空:

  • 如果通信失败时响应内容为null,则IOException可能是合适的。

  • 如果响应内容永远不能为空(根据文档),则用户在您的代码 and/or 基础 class 库中遇到了错误。在这种情况下,发出断言失败信号的异常将是最合适的。不幸的是,核心库中有 none,所以我个人(绝对)使用 InvalidOperationException。自定义 LogicErrorExceptionAssertionFailedExceptionShouldNeverHappenException 将是更简洁的解决方案。