ArgumentException的正确使用方法是什么?

13

据我所见,ArgumentExceptions通常像这样使用:

public void UpdateUser(User user)
{
    if (user == null) throw new ArgumentException("user");
    // etc...
}

但是如果我有像这样的内容会怎样:

public void UpdateUser(int idOfUser)
{
    var user = GetUserById(idOfUser);
    if (user == null) throw new ArgumentException("idOfUser");
    // etc...
}

那仍然是一个ArgumentException吗?


10
从技术上讲,第一个应该是“ArgumentNullException”... - xanatos
6
第二个问题,在这里(评论中)他们建议使用ObjectNotFoundException - xanatos
2
@xanatos - 这两个评论组成了一个答案。 - H H
1
如果说有什么区别的话,第二个例子更像是ArgumentException而不是第一个例子(应该是ArgumentNullException)。 - H H
在您的第二种情况中,我只能想到一个原因,您希望抛出ArgumentException(或派生类),那就是如果该方法可以确定接收到的idOfUser超出范围(负数、零...取决于您的数据库规格)。然后,您将希望抛出ArgumentOutOfRange异常。除此之外,请优先考虑设计一个生成自定义操作结果对象以指示成功或失败(以及后者原因)的设计。 - Crono
1
ArgumentException及其派生类是开发人员错误,因此在运行时绝不应该处理它们。永远不要以一种强制使用开发人员依赖于处理ArgumentException来处理可能缺失的数据的方式设计API。这不是这些异常存在的目的。 - Crono
2个回答

8

第一个

if (user == null) throw new ArgumentException("user");

应该是

if (user == null) throw new ArgumentNullException("user");

如果可能的话,不应直接抛出ArgumentException

ArgumentException 的主要派生类是 ArgumentNullExceptionArgumentOutOfRangeException。除非派生类都不可接受,否则应该使用这些派生类,而不是使用 ArgumentException

对于第二个例子,在这里Should I throw a KeyNotFoundException for a database lookup?,他们建议(在评论中)。
if (user == null) throw new ObjectNotFoundException();

这在System.Data中定义:System.Data.ObjectNotFoundException


它位于System.Data命名空间中,但需要将Entity dll添加到项目中。 - Rhs
我有一个具有参数(DateTime?minDate,DateTime?maxDate,bool includeMinDate,bool includeMaxDate)的验证方法。 如果调用方同时指定minDate和maxDate,则如果minDate大于maxDate,则会引发ArgumentException,并且如果minDate等于maxDate并且includeMinDate和includeMax date都不为true(即在上限和下限相等时不可将下限包含而上限排除(反之亦然)),则还会引发ArgumentException。 对于组合,ArgumentException也是适当的。 - Triynko

1
作为名称所示,ArgumentException 是关于参数的异常。这意味着该参数在本质上存在错误。
一般形式如下:
public void SomeMethod(SomeType arg)
{
  if(!TestArgValid(arg))
    throw new ArgumentException("arg"); //Or more specific is possible
                                        //e.g. ArgumentNullException
    /* Actually do stuff */
}

如果 GetUserById 失败的唯一可能方式是 idOfUser 的值本身有错误,那么以下两种情况在实践中都是相同的:
public void UpdateUser(int idOfUser)
{
  if(!TestValid(idOfUser))
    throw new ArgumentException("idOfUser");
  var user = GetUserById(idOfUser);
  // Do stuff with user
}

public void UpdateUser(int idOfUser)
{
  var user = GetUserById(idOfUser);
  if(user == null)
    throw new ArgumentException("idOfUser");
  // Do stuff with user
}

如果事实证明在测试idOfUser之前比之后测试user更快或更节省资源,且调用GetUserById没有副作用,并且这种差异确实有意义,那么也许第二个版本是对第一个版本的合理优化。

但是,这仅适用于上述所有条件都成立的情况下,并且这是一种检测具有某些特定优势的无效参数的奇怪方式,在隐藏其他所有内容中的奇怪行为时,我们从方法的封装中获益。

很有可能存在一个有效的idOfUser,而没有相应的user,在这种情况下,它肯定不是参数异常。


嗨Jon,感谢您提供这个旧的代码片段,但是修复代码中ArgumentException的使用是否值得呢?在所示的构造函数中,字符串用于指定消息,而不是参数的名称(https://learn.microsoft.com/en-us/dotnet/api/system.argumentexception.-ctor?view=net-6.0#System_ArgumentException__ctor_System_String_)。 - Dave Watts

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接