问题 如果列表/集合为空或为null且无法迭代(不是参数),则抛出什么异常类型?


假设一个简单的示例,其中方法检索集合(例如包含一些配置字符串的列表)并尝试以某种方式检查它:

void Init()
{
    XmlDocument config = new XmlDocument();
    config.Load(someXml);
    var list = config.SelectNodes("/root/strings/key"); // Normally, list should not be null or empty

    if (list == null || list.Count == 0)
        throw new SomeExceptionType(message);   // What kind of exception to throw?

    // Iterate list and process/examine its elements
    foreach (var e in list) ...
}

在此特定实例中,如果未检索到任何内容,则该方法无法正常继续。我不确定在这种情况下抛出什么异常类型。据我所知,我的选择是:

  • 手动扔掉任何东西,让 NullReferenceException 被抛出 自动(不处理空列表情况),

  • 抛出自定义异常类型(可能不是一个好主意,因为我预计调用者不会尝试对异常做任何事情, 即他不会寻找特定的异常类型来处理),

  • 做点别的吗?

11150
2017-09-05 13:40


起源

是 /root/strings/key 修复还是变量?然后我会用 ArgumentException 或使用自定义异常类型。 - Tim Schmelter
这是固定的,但不应该 ArgumentException 只能在提供给方法的无效参数的上下文中使用(在这种情况下,根本没有参数)? - w128
如果来源是你可以抛出的财产 ArgumentException 因为setter获得了无效的参数 value 会无效的。 - Tim Schmelter


答案:


您可以为适当的逻辑创建自己的异常类型:

public class InitializationException : Exception
{
}

接着:

throw new InitializationException {Message = "Collection is empty"};

10
2017-09-05 13:45



我认为如果你愿意,这是抛出异常的最好方法。如果你没有抛出一个只有调用者使用NullReferenceException处理它的异常 - Jordy van Eijk
我不喜欢导出只在名称上有所不同并且根本不会被处理的自定义异常1)代码组织问题(如果你在多个项目中有相同的异常条件,你在哪里放置所有这些声明等) 2)这真的与简单的异端和抛出一般异常(消息)有什么不同吗? - w128
我建议您使用类型异常而不是一般的异常,因为稍后您可以轻松地分割捕获逻辑。 - Dmitry Martovoi
还应该注意,自定义异常应该支持序列化并实现四个基本构造函数。看到 这个 和 这个 - w128


我不确定在这种情况下你可以优雅地抛出一个内置异常... a NullReferenceException 是不合适的,因为空列表不是空引用

我建议使用Dmintry提出的解决方案,因为调用者仍然可以使用 try...catch(Exception) 不必知道或关心异常是真的 SuperDooperListNullOrEmptyFunTimeException

因为从调用者的角度来看这是一个不可恢复的错误(即他们无法控制所选的Xml路径,并且无法控制正在加载的XML),所以异常只会被转储到日志中或在屏幕上供人类消费,此时它没有实际意义 - 因为实际的信息比类型更重要。

另一方面,如果它是可恢复的(调用者可以在确保加载的xml现在包含正确格式化的xml之后重新尝试该方法,或者调用者可以通知用户并要求他们去修复XML并且“你想现在重试吗?“那种事情,那么你 需要 给他们一个打字的例外,这样他们就知道重试是安全的,而不是一个普通的旧例外,这可能意味着其他事情发生了可怕的错误,重试只会让事情变得更糟......


2
2017-09-05 15:01



有一个 - ConfigurationErrorsException。 NullReferenceException 是不合适的,因为你不应该抛出,抓住或留下你的代码抛出它。 - dstarkowski
@dstarkowski - 有趣的一点,虽然看起来更像是一个框架异常,特别是在加载app / web.config设置的上下文中 - 我的答案是假设这是某种自定义xml有效负载。并且特别提到该类不应该由用户代码实例化。不过,值得考虑的是肯定,当然如果这是加载“经典”.config设置然后是,它应该冒出来给调用者。 - Stephen Byrne


这不是编程问题,因为它是一个设计问题,.NET列表对象在它们为空时不抛出异常的原因是因为很多情况下空列表是可预期和可接受的情况。

如果在上下文中,您使用的列表永远不会为空,则抛出异常(自定义的)

但是,如果列表可能是空的可能且合乎逻辑,那么为什么要中断整个事情,除了例外情况之外,需要例外?一个 foreach 循环和空列表不会抛出异常,循环根本不会循环。

至于零可能性(非常罕见) SelectNodes 如果理解得很好)这是同样的问题,在一些库或函数返回一个 null 是正常的行为而不是例外。


1
2017-09-05 13:55



我知道你的意思,但在这种特殊情况下,我感兴趣的是必须列表不是空/空的情况,因为这意味着正确的初始化/配置所需的步骤根本无法执行(例如,关键系统配置文件已损坏或丢失,无法使用默认值)。 - w128
那么你回答了自己的问题,这是一种特殊的情况,在正常流程之外,它应该得到例外。 - Simon Rapilly
@ w128添加到我说的内容,它应该得到一个例外,但我认为你不会发现任何适合这种情况,所以自定义异常就是 - Simon Rapilly