我应该抛出 ArgumentException 吗?

Should I throw an ArgumentException?

这个问题是关于约定和解释 MSDN 的,所以我不认为它主要基于意见。

我想知道 ArgumentException:我有一个构建器 class,它用于构建一个过滤器对象,该对象将在从邮箱中检索一组电子邮件时应用。构建器有多种方法来设置过滤器选项。例如,我有两种设置过滤器"sent date"范围的方法——在XX之前发送and/or在XX之后发送。我想为每个子句添加一个保护子句,如果提供的 "before" 日期是 提供的 "after" 日期之后,这将引发异常。我会用一种常见的验证方法来做到这一点:

/// <summary>
/// This class provides methods for validating dates in various ways.
/// </summary>
internal static class DateValidation
{
    /// <summary>
    /// Validate the provided "start" and "end" date/time values.
    /// If they do not represent a valid range, throw an exception.
    /// </summary>
    /// <param name="start">The date/time that represents the start of the range.</param>
    /// <param name="end">The date/time that represents the end of the range.</param>
    internal static void ValidateDateTimeRange(DateTime? start, DateTime? end)
    {
        if (start.HasValue && end.HasValue)
        {
            if (start.Value > end.Value) 
                throw new Exception(
                string.Format(@"The start date/time ""{0}"" " +
                @"occurs after the end date/time ""{1}"".", 
                start.ToString(), end.ToString()));
        }
    }
}

以下是两种生成器方法:

/// <summary>
/// Set a value which represents a date/time after which
/// messages must have been sent to be part of filtered output.
/// </summary>
/// <param name="afterDateTime">The date/time after which
/// messages must have been sent to be part of filtered output.</param>
public void SetSentAfterDateTime(DateTime afterDateTime)
{
    DateValidation.ValidateDateTimeRange(afterDateTime, _sentBeforeDateTime);
    _sentAfterDateTime = afterDateTime;
}

/// <summary>
/// Set a value which represents a date/time before which
/// messages must have been sent to be part of filtered output.
/// </summary>
/// <param name="beforeDateTime">The date/time before which
/// messages must have been sent to be part of filtered output.</param>    
public void SetSentBeforeDateTime(DateTime beforeDateTime)
{
    DateValidation.ValidateDateTimeRange(_sentAfterDateTime, beforeDateTime);
    _sentBeforeDateTime = beforeDateTime;
}

根据MSDN

Most commonly, an ArgumentException is thrown by the common language runtime or another class library and indicates developer error.

我知道短语 "most commonly" 为其他用法留下了可能性,就像我的用法一样,但我喜欢坚持惯例。我正在构建一个 public API,因此将记录异常并将在 public 接口之外冒泡;此外,它不会 "indicate developer error"。它 可以 可以想象地指示用户错误(使用异常来处理用户输入验证问题是一种常见的约定)。不过,根据 MSDN 的描述,我感觉这不是它的目的。

...Derived classes [ArgumentNullException and ArgumentOutOfRangeException] should be used instead of ArgumentException, except in situations where neither of the derived classes is acceptable. For example, exceptions should be thrown by:

...

ArgumentOutOfRangeException when the value of an argument is outside the range of acceptable values; for example, when the value "46" is passed as the month argument during the creation of a DateTime.

我的论点可能超出可接受值的范围,但该条件是根据其他 date/time 值动态确定的。没有 "out of range".

的静态值范围

那么 ArgumentException 通常用于这种情况吗?

来自 Krzysztof Cwalina 的 How to Design Exception Hierarchies

A usage error is something that can be avoided by changing the code that calls your routine.

我们同意这是一个使用错误。

Eric Lippert 称他们为 Boneheaded exceptions:

Boneheaded exceptions are your own darn fault, you could have prevented them and therefore they are bugs in your code. You should not catch them; doing so is hiding a bug in your code.

开发人员不应 catch 这些类型的错误,而应更改调用代码以确保异常永远不会发生。这就是为什么确切的异常类型并不重要。

正如 Cwalina 解释的那样:

Usage errors need to be communicated to the human developer calling the routine. The exception type is not the best way to communicate errors to humans. The message string is a much better way. Therefore, for exceptions that are thrown as a result of usage errors, I would concentrate on designing a really good (that is explanatory) exception message and using one of the existing .NET Framework exception types: ArgumentNullException, ArgumentException, InvalidOperationException, NotSupportedException, etc. In other words, don’t create new exception types for usage errors because the type of the exception does not matter for usage errors. The .NET Framework already has enough types (actually too many, IMHO) to represent every usage error I can think of.

(强调我的)

使用 ArgumentException 还是 InvalidOperationException 并不重要。选择一个,但要确保它有明确的 Message.

我同意接受的答案;我要补充的是,如果您的代码将您的调用方置于 必须 捕获异常以确定用户提供的数据是否有效的位置,那么您就完成了我所说的一个"vexing exception"。令人烦恼的是,非异常情况还得自己做一个异常处理器来处理。用户输入不一致的日期范围并非例外。这是程序工作流程的正常部分。

考虑在用户界面层解决问题。实施日期选择器,以便它们不允许用户选择会产生异常的日期组合。有多种方法可以做到这一点——例如,将无效的日期灰显。或者检测不一致并使选择器变红或闪烁或其他任何东西,并阻止操作被提交。