【问题标题】:What exception type to use when a property cannot be null?当属性不能为空时使用什么异常类型?
【发布时间】:2010-12-02 01:54:10
【问题描述】:

在我的应用程序中,如果特定类的属性为 null 或空(如果它是字符串),我需要抛出异常。我不确定在这种情况下使用的最佳例外是什么。我不想创建一个新的异常,我不确定 ArgumentNullException 在这种情况下是否合适。

我应该创建一个新异常还是有一个可以使用的异常?

我不介意抛出 ApplicationException。

【问题讨论】:

标签: c# .net exception


【解决方案1】:

构造函数是否将其设置为非空值?如果是这样,我会从二传手中抛出ArgumentNullException

【讨论】:

    【解决方案2】:

    我会抛出一个InvalidOperationException。 MSDN 说它“当方法调用对对象的当前状态无效时抛出。”

    【讨论】:

    • 我认为这不适合这种情况。只有在被调用的对象无效时才应使用 InvalidOperationException。在这种情况下,被调用的对象是 100% 有效的,它是无效的方法的参数。
    • 如果对象处于使用属性设置器完全无效的状态,您将抛出InvalidOperationException。也就是说,操作本身是无效的。 MSDN 文档给出了“写入已打开以供阅读的System.IO.FileStream”的示例。
    • 我想我们不知道被调用的确切场景。我假设我们正在调用一个类的方法,并尝试在方法中使用该类的属性。在这种情况下,对象对于正在调用的方法无效(因为需要在调用方法之前设置属性)。看起来@JaredPar 正在考虑参数上的属性未初始化的情况。在这种情况下,他对ArgumentException 的建议是最有意义的。
    • 我应该补充一点,我之前的评论纯粹是考虑属性设置器。未分配给有效值的属性确实是无效状态的示例。我认为您不应该从属性获取器中抛出该异常......还有其他选择。
    • 如果封装对象的状态因为您设置的属性无效而变得无效/不起作用,那么我认为 InvalidOperationException 是完全可以接受的。但是在这种情况下,我猜应该通过构造函数注入该属性,因为封装对象依赖于该属性以履行其单一职责。
    【解决方案3】:

    好吧,如果你引用一个类的属性,这不是一个参数。因此,您不应使用 ArgumentException 或 ArgumentNullException。

    如果您不理会事情,就会发生 NullReferenceException,所以我认为这不是您想要的。

    因此,使用 ApplicationExeption 或 InvalidOperationException 可能是您最好的选择,确保提供一个有意义的字符串来描述错误。

    【讨论】:

    • 我很确定 .NET Framework 中有一些类会从属性的设置器中抛出 ArgumentException。我不记得具体在哪里,但我记得遇到过它。您可能会争辩说它可能不“正确”,但无论框架是否开创了先例。
    【解决方案4】:

    如果有人试图分配 null,抛出 ArgumentNullException 是非常合适的。

    属性不应该引发读取操作。

    【讨论】:

    • 你从哪里得到一个属性永远不应该抛出读取操作的想法?这当然很容易发生。
    • @John 我不认为他的意思是它不能,而只是它不应该。
    • @John Fisher:Cwalina、Krzystof 和 Brad Abrams:“框架设计指南。可重用 .Net 库的约定、习语和模式”,Addison-Wesley,2006 年。 121 - 在其他来源中。
    • 啊。我当然理解“避免从属性获取器中抛出异常”,但这与“不应该......”完全不同。
    【解决方案5】:

    如果问题是参数的成员而不是参数本身为空,那么我认为最好的选择是更通用的ArgumentExceptionArgumentNullException 在这里不起作用,因为参数实际上不为空。相反,您需要更通用的“您的论点有问题”异常类型。

    构造函数的详细消息在这里非常合适

    【讨论】:

    • “构造函数的详细信息”是什么意思?我必须有一个默认构造函数。
    • @Vadim 关于抛出参数异常的详细原因。例如“参数 foo 的属性 Blah 必须为非空”
    • @Vadim,他指的是异常的构造函数,而不是你对象的构造函数。
    【解决方案6】:

    如果它不能为 null 或为空,请让您的 setter 不允许 null 或空值,或者在这种情况下抛出 ArgumentException。

    另外,要求在构造函数中设置属性。

    这样你强制一个有效值,而不是稍后回来说你不能确定帐户余额,因为帐户没有设置。

    但是,我同意 bduke 的回应。

    【讨论】:

    • 我必须有默认构造函数。
    • 你不能在构造函数中放一些我取的有效默认值吗?
    • 很遗憾我不会玩猜谜游戏。我不知道应该在运行时设置什么值。
    • 那么 InvalidOperationException 将是你最好的选择。
    【解决方案7】:

    MSDN guidelines for standard exceptions 声明:

    属性的隐含值参数的名称一定要使用value 二传手。

    以下代码示例显示了一个 抛出异常的属性,如果 调用者传递一个空参数。

    public IPAddress Address
    {
        get
        {
            return address;
        }
        set
        {
            if(value == null)
            {
                throw new ArgumentNullException("value");
            }
            address = value;
        }
    }
    

    另外,MSDN guidelines for property design 说:

    避免从 属性获取器。

    属性获取器应该很简单 没有任何先决条件的操作。 如果 getter 可能抛出异常, 考虑重新设计该物业 成为一种方法。这个建议确实 不适用于索引器。索引器可以 由于无效而抛出异常 论据。

    抛出是有效且可接受的 属性设置器的异常。

    所以在null 的setter 中抛出ArgumentNullException,在空字符串上抛出ArgumentException,在getter 中什么也不做。由于 setter 抛出并且只有您可以访问支持字段,因此很容易确保它不会包含无效值。让 getter throw 是没有意义的。不过,这可能是使用Debug.Assert 的好地方。

    如果你真的不能提供合适的默认值,那么我想你有三个选择:

    1. 只需返回属性中的任何内容并将此行为记录为使用合同的一部分。让调用者处理它。您可能还需要构造函数中的有效值。不过,这可能完全不适合您的应用程序。

    2. 用方法替换属性:一个 setter 方法在传递一个无效值时抛出,一个 getter 方法在该属性从未被分配一个有效值时抛出 InvalidOperationException

    3. 从 getter 中抛出 InvalidOperationException,因为您可以将“从未分配过属性”视为无效状态。虽然您通常不应该从 getter 中抛出异常,但我想这可能是一个很好的例外处理理由。

    如果您选择选项 2 或 3,您还应该包含一个 TryGet- 方法,该方法返回一个 bool,指示该属性是否已设置为有效值,如果是,则在 out 参数中返回该值.否则,您会强制调用者准备好处理InvalidOperationException,除非他们之前自己设置了属性,因此知道它不会抛出。比较 int.Parseint.TryParse

    我建议将选项 2 与 TryGet 方法一起使用。它不违反任何准则,并且对调用代码的要求最低。


    关于其他建议
    ApplicationException 太笼统了。 ArgumentException 对于null 来说有点太笼统了,但其他方面都很好。 MSDN docs again:

    一定要抛出最具体(最衍生)的异常,即 合适的。例如,如果一个方法 收到一个空值(Visual 中没有 基本)参数,它应该抛出 System.ArgumentNullException 改为 其基本类型 System.ArgumentException。

    事实上你根本不应该使用ApplicationException (docs):

    一定要从 T:System.Exception 类而不是 T:System.ApplicationException 类派生自定义异常。

    最初认为自定义异常应该派生自 ApplicationException 类;但是,尚未发现这会增加显着的价值。有关详细信息,请参阅处理异常的最佳实践。

    InvalidOperationException 不是用于方法或属性的参数无效的情况,而是用于整个操作 无效的情况 (docs)。它不应该从 setter 中抛出:

    如果处于不适当的状态,请抛出 System.InvalidOperationException 异常。如果给定对象的当前状态,属性集或方法调用不合适,则应引发 System.InvalidOperationException。例如,写入已打开以供读取的 System.IO.FileStream 应引发 System.InvalidOperationException 异常。

    顺便说一句,InvalidOperationException 是用于当操作无效时对象的当前状态。如果操作对于整个类总是无效,则应使用NotSupportedException

    【讨论】:

    • @Joren,@Vadim 表示他需要一个默认的指导员,并且该字段没有合理的默认设置,因此仅监控 setter 对他的场景来说是不够的。
    • 我同意这里所说的一切。我只想补充一下ApplicationException,如果您像任何业务软件状态异常一样将其用于 1 个全面的目的,那将是一个不错的选择。不是系统不能做到这一点,而是它不会。如果你把ApplicationException 扔给所有东西,那就太糟糕了,但我宁愿只使用AE,也不愿继承它。继承只是重命名某些东西是浪费精力。
    【解决方案8】:

    有一个先例将 ArgumentNullException 的解释延伸为“字符串参数为 null 或空”:在这种情况下,System.Windows.Clipboard.SetText 将抛出 ArgumentNullException。

    所以我认为在属性设置器中使用它而不是更通用的 ArgumentException 不会有任何问题,只要您记录它。

    【讨论】:

      【解决方案9】:

      只要错误消息对开发人员有帮助,就抛出任何东西。无论如何,这类异常绝不应该发生在开发之外。

      【讨论】:

        猜你喜欢
        • 2020-10-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-11-21
        • 2020-09-30
        • 2020-06-02
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多