【问题标题】:What exception type to throw if a list/collection is empty or null and cannot be iterated (not a parameter)?如果列表/集合为空或 null 且无法迭代(不是参数),则抛出什么异常类型?
【发布时间】:2013-09-09 09:13:14
【问题描述】:

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

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被抛出 自动(不处理空列表情况),

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

  • 做点别的吗?

【问题讨论】:

  • /root/strings/key 是修复还是变量?然后我会使用ArgumentException 或使用自定义异常类型。
  • 它是固定的,但ArgumentException不应该只用于提供给方法的无效参数的上下文中(在这种情况下,根本没有参数)?
  • 如果源是一个属性,你可以抛出一个ArgumentException,因为setter的参数无效,因此value是无效的。

标签: c# exception-handling


【解决方案1】:

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

public class InitializationException : Exception
{
}

然后:

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

【讨论】:

  • 如果你愿意,我认为这是抛出异常的最佳方式。如果你不抛出一个异常,只是由调用者用 NullReferenceException 处理它
  • 我不喜欢派生仅名称不同且根本不会处理的自定义异常是 1) 代码组织问题(如果您有相同的声明,您将所有这些声明放在哪里跨多个项目的异常条件等)2)这真的与简单地成为异端并抛出一般异常(消息)有什么不同吗?
  • 我建议你使用类型异常而不是一般异常,因为以后你可以很容易地拆分捕获逻辑。
  • 可能还应该注意,自定义异常应该支持序列化并实现四个基本构造函数。见thisthis
【解决方案2】:

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

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

如果列表可能为空是可能且合乎逻辑的,为什么要中断整个事情,它是例外而不是例外,所以需要一个例外? foreach 循环和空列表不会引发异常,循环根本不会循环。

至于 null 的可能性(如果理解良好,SelectNodes 非常罕见)这是同样的问题,在某些返回 null 的库或函数中是正常行为而不是异常。

【讨论】:

  • 我知道你的意思,但在这种特定情况下,我对列表必须不为空/空的情况感兴趣,因为这意味着正确初始化/配置所需的步骤根本无法执行(例如,关键的系统配置文件损坏或丢失,无法使用默认值)。
  • 那么你回答了你自己的问题,这是一个例外情况,在正常流程之外,它应该例外。
  • @w128 补充我所说的,它应该有一个例外,但我认为你不会找到任何适合这种情况的,所以自定义例外是
【解决方案3】:

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

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

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

另一方面,如果它是可恢复的(调用者可以在确保要加载的 xml 现在包含正确格式的 xml 后重试该方法,或者调用者可以通知用户并要求他们去修复XML 和“你想现在重试吗?”之类的东西)然后你需要给他们一个类型化的异常,这样他们就知道重试是安全的,而不是一个普通的旧异常,这可能意味着别的东西出了大错,重试只会让事情变得更糟......

【讨论】:

  • 有一个 - ConfigurationErrorsExceptionNullReferenceException 不合适,因为您不应该抛出、捕获或让代码抛出它。
  • @dstarkowski - 有趣的一点,虽然这看起来更像是一个框架异常,特别是在加载 app/web.config 设置的上下文中 - 我的回答是假设这是某种自定义 xml 有效负载。并且特别提到这个类不应该被用户代码实例化。尽管如此,确实值得考虑,如果这是加载“经典” .config 设置,那么是的,应该将它冒泡给调用者。
【解决方案4】:

Enumerable.First 抛出 System.InvalidOperationException 如果集合为空。我猜你也可以。

throw new InvalidOperationException("Sequence contains no elements");

https://docs.microsoft.com/en-us/dotnet/api/system.linq.enumerable.first?view=netframework-4.8

【讨论】:

    猜你喜欢
    • 2019-11-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多