【问题标题】:Should I Throw ArgumentNullException if a string is blank?如果字符串为空,我应该抛出 ArgumentNullException 吗?
【发布时间】:2009-08-31 05:42:17
【问题描述】:

我正在研究一种在给定字符串参数的情况下执行某些操作的方法。字符串参数的有效值是 null 或 string.Empty 以外的任何值。所以我的代码看起来像这样。

private void SomeMethod(string someArgument)
{
    if(string.IsNullOrEmpty(someArgument))
        throw new ArgumentNullException("someArgument");

    // do some work
}

那里没有什么太令人兴奋的了。我的问题是,即使字符串等于 string.Empty,也可以抛出 ArgumentNullException 吗?因为从技术上讲它不是空的。如果你认为它不应该抛出 ArgumentNullException,那么应该抛出什么异常?

【问题讨论】:

  • 当字符串为 "" 时抛出 ArgumentNullException 具有误导性。
  • 我同意,唉,我以前这样做的原因是因为“乔”给出的答案,它在 .net 框架代码中是这样使用的。

标签: .net


【解决方案1】:

ArgumentException 应该被抛出 String.Empty 案例。这将表明一个问题,而不是它为空。为了避免出现NullReferenceException,我首先检查 null,然后修剪并检查空大小写以防止任何空格通过。

private void SomeMethod(string someArgument)
{
    if(someArgument == null)
        throw new ArgumentNullException("someArgument");

    if (someArgument.Trim() == String.Empty)
        throw new ArgumentException("Input cannot be empty", "someArgument");

    // do some work
}

从 .NET 4.0 开始,您可以使用 String.IsNullOrWhiteSpace 方法一次性执行这些检查。通过这样做,您放弃了指定精细异常类型的能力,因此我会选择 ArgumentException 并相应地更新消息。

【讨论】:

  • 我使用的是 C# 2.0,对 InvalidArgumentException 的唯一引用是在 Microsoft.SqlServer.Management.Common 命名空间中,是您推荐的还是您建议我创建自己的 InvalidArgumentException 类?跨度>
  • @Keith:你是对的,我的错。如果您愿意,可以自己编写或使用框架提供的 ArgumentException。我将进行编辑以反映正确的名称。
  • ArgumentException 构造函数没有像 ArgumentNullException 这样的“paramName”参数。所以 'throw new ArgumentException("paramName")' 可能会造成混淆,因为它没有给出任何说明参数有什么问题的迹象。您应该提供类似的“消息”参数(“someArgument 可能不是空字符串”)。在国际应用程序中,此消息需要本地化。因此,只有在确实需要区分空字符串和空字符串的情况下,我才会遇到所有这些麻烦。
  • @Joe:在这种情况下,采用 2 个字符串参数的构造函数可能更合适,第一个是错误消息,第二个是导致异常的参数 (msdn.microsoft.com/en-us/library/sxykka64.aspx)。这将导致: throw new ArgumentException("Input cannot be empty", "someArgument");
  • 您的第一个语句和您的代码彼此不一致,大概来自对您答案的编辑?您为 string.Empty 情况抛出 ArgumentNullException,并且只为非空空白字符串抛出 ArgumentException。
【解决方案2】:

如果空字符串不是您的方法可接受的输入,您应该抛出ArgumentException。如果您在客户没有提供null 参数的情况下抛出ArgumentNullException,这可能会让客户非常困惑。

这只是另一个用例。您可能还有不接受空输入值但接受空字符串的方法。在整个应用程序中保持一致很重要。

【讨论】:

  • 我在考虑 ArgumentOutOfRangeException 但可能用于数组索引范围异常。
  • 对于数组索引异常,使用IndexOutOfRangeException。仅当您的方法被记录为仅接受特定的字符串集合时才应使用 ArgumentOutOfRangeException(例如:“abc”、“def”、“ghi”是唯一接受的输入)。
  • Microsoft 自己的文档似乎在 IndexOutOfRangeException/ArgumentOutOfRangeException 主题上提供了相互矛盾的建议。 docs.microsoft.com/en-us/dotnet/api/… 也许这就是他们的发展方向,但我倾向于同意 @ronald-wildenberg 关于他们的使用
【解决方案3】:

考虑到所有已经说过的事情(Joe / Ahmad Mageed),我会为那个案例创建一个例外。

class ArgumentNullOrEmptyException : ArgumentNullException

【讨论】:

  • 从 ArgumentException 派生不是更好吗?在 OOP 中,说“A 或 B”是“A”似乎并不正确。
  • @dcstraw ArgumentNullException 派生自 ArgumentException。在这种情况下,我认为 ArgumentNullOrEmptyException 从 ArgumentNullException 派生更好。
  • 另外我认为它的名字应该是 StringArgumentNullOrEmptyException 因为它只对字符串参数有意义。
  • 或者...如果它也处理ICollection,它的名字可以是ArgumentNullOrEmptyException。
  • ArgumentNullOrEmptyException : ArgumentException 在您想要捕获 ArgumentNullException 的极少数情况下,程序员应该区别对待这种情况,因为它可能为空。例如。使用 C# 8,您可能需要抛出、捕获和包装/重新抛出由传入 null 的外部代码引起的 ArgumentNullException,其中您的代码假定为不可为空的输入。来自空字符串的 ArgumentException 不会以相同的方式处理
【解决方案4】:

ArgumentNullException 有时在 .NET Framework 中用于 String.IsNullOrEmpty 案例 - 例如System.Windows.Forms.Clipboard.SetText。

所以我认为在你的代码中做同样的事情是合理的,除非区分这两种情况有一些真正的价值。

请注意,从 ArgumentException 派生的此异常和其他异常通常表示编程错误,因此需要提供帮助开发人员诊断问题所需的信息。就我个人而言,我认为如果您将 ArgumentNullException 用于空字符串参数,开发人员不太可能会感到困惑,特别是如果您按照下面的示例记录此行为。

/// <summary>
/// ... description of method ...
/// </summary>
/// <param name="someArgument">... description ...</param>
/// <exception cref="ArgumentNullException">someArgument is a null reference or Empty.</exception>
public void SomeMethod(string someArgument)
{
   ...
}

【讨论】:

  • 我不会在 Windows 窗体中寻找 .NET Framework 代码中最新最佳实践的示例。我能够在 File.Copy(string, string) 中快速找到一个反例,它为 null 抛出 ArgumentNullException,为空抛出 ArgumentException。
【解决方案5】:

老问题,但由于 Google 对它的排名很高,这里为未来的读者提供另一个(更好的!)选项:ArgumentOutOfRangeException。

ArgumentException 是 ArgumentNullException 和 ArgumentOutOfRangeException 等的基类。这意味着对于处理异常的开发人员来说,它更通用且信息量更少。换句话说,虽然所有ArgumentNullExceptions 和ArgumentOutOfRangeExceptions 也是ArgumentExceptions,但反之则不然。

ArgumentOutOfRangeException 是一个更具体的例外,它表示“您提供的值不在我预期的值范围内”,这正是您试图告诉其他开发人员的内容。如果您不接受空字符串,这是最好的标准异常类型。

或者,如果区分值真的很重要,请创建您自己的从 ArgumentNullException 派生的异常类型。

在处理您的异常时,其他不需要区分他们提供的值为什么错误的开发人员可以直接捕获 ArgumentException 并一次性获取所有信息。

【讨论】:

  • 在@ronald-wildenberg 的answer 下讨论了为什么不使用ArgumentOutOfRangeException。这是否会改变您对此的看法?
  • 简而言之,没有。该答案断言“只有当您的方法被记录为仅接受特定的字符串集合时,您才应该使用ArgumentOutOfRangeException”,但没有说明为什么“范围”应定义为“特定集”。一个特定的集合是一个范围,就像“除 A、B 之外的所有东西......”等。就我个人而言,我也会使用异常消息来解释为什么在这种特定情况下该值被认为超出范围。但从本质上讲,如果您的方法不处理特定的输入值,则它是“超出范围”的预期/处理值。
【解决方案6】:

这要视情况而定。

问题归结为,这真的是一个错误吗?我的意思是你总是期待一个价值吗?如果你这样做了,那么你最好的选择可能是创建你自己的Exception,也许像这样:

class StringEmptyOrNullException : Exception
{
}

您还可以在其中添加自己的构造函数和添加的信息等。

但是,如果它不是您的程序中发生的“异常”,那么从方法返回 null 并从那里处理它可能是一个更好的主意。请记住,Exception 用于特殊情况。

希望对你有帮助,

凯尔

【讨论】:

  • 如果您确实创建了自己的异常,它应该派生自 ArgumentException 而不是 Exception。
  • 确实取决于上下文,在这种情况下是的。如果你想在其他地方使用它,你让它更通用。例如,如果您有一个生成不应为空的字符串的方法,则 ArgumentException 将不是最佳选择。因此,根据您对它的使用量以及使用它的上下文,您将决定从什么继承。
  • IMO 这是两种不同的情况 - 生成字符串的方法和传递给方法的参数,我希望有两种不同的异常类型。最好是冗长而不是把多种错误情况塞进一个异常中。
【解决方案7】:

为什么不使用这段代码?

private void SomeMethod(string someArgument)
{
//chek only NULL
if(ReferenceEquals(someArgument,null))
    throw new ArgumentNullException("someArgument");

// and after trim and check
if (someArgument.Trim() == String.Empty)
    throw new ArgumentException("Input cannot be empty", "someArgument");

// do some work
}

【讨论】:

    猜你喜欢
    • 2017-07-02
    • 1970-01-01
    • 1970-01-01
    • 2013-05-20
    • 2021-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多