19

在验证方法的输入时,我曾经检查参数是否为空,如果是,我会抛出 ArgumentNullException。我对列表中的每个参数都执行此操作,因此最终得到如下代码:

 public User CreateUser(string userName, string password, 
                            string Email, string emailAlerts, 
                            string channelDescription)
    {

        if (string.IsNullOrEmpty(userName))
            throw new ArgumentNullException("Username can't be null");

        if (string.IsNullOrEmpty(Email))
            throw new ArgumentNullException("Email can't be null");
       //etc, etc, etc
    }

这个可以吗?我为什么要这样做?如果我只是将所有检查分组并返回一个空值而不是抛出异常,可以吗?解决这种情况的最佳做法是什么?

PS:我想改变这一点,因为方法很长,这样做很乏味。
想法?

4

8 回答 8

17

用这样的东西制作一个 ArgChecker 类

  ArgChecker.ThrowOnStringNullOrEmpty(userName, "Username");

ThrowOnStringNullOrEmpty 在哪里

  public static void ThrowOnStringNullOrEmpty(string arg, string name)
  {
      if (string.IsNullOrEmpty(arg))
        throw new ArgumentNullException(name + " can't be null");
  }

您还可以尝试使用 params arg 处理参数列表,例如:

  public static void ThrowOnAnyStringNullOrEmpty(params string[] argAndNames)
  {
       for (int i = 0; i < argAndName.Length; i+=2) {
          ThrowOnStringNullOrEmpty(argAndNames[i], argAndNames[i+1]);
       }
  }

像这样打电话

  ArgChecker.ThrowOnAnyStringNullOrEmpty(userName, "Username", Email, "email");
于 2009-07-21T11:26:43.733 回答
17

我使用并且可能从 NHibernate 源中获取的一种方法是创建一个静态类Guard,使用如下:

public void Foo(object arg1, string arg2, int arg3)
{
    Guard.ArgumentNotNull(arg1, "arg1");
    Guard.ArgumentNotNullOrEmpty(arg2, "arg2");
    Guard.ArgumentGreaterThan(arg3, "arg3", 0);
    //etc.
}

public static class Guard
{
    public static void ArgumentNotNull(object argument, string parameterName)
    {
        if (parameterName == null)
            throw new ArgumentNullException("parameterName");

        if (argument == null)
            throw new ArgumentNullException(parameterName);
    }
    //etc.
}

这在方法开始时减少了很多干扰,并且表现良好。

于 2009-07-21T11:27:50.720 回答
9

您应该考虑该方法,它需要做什么以及使用什么数据。如果空值表示实际的故障情况,请使用异常。如果空值是可接受的,则接受它们。

考虑按合同设计的原则,特别是您的功能的先决条件是什么,并标准化执行它们的方法(Matt 和 Lou 都在他们的答案中提出,所以我不需要详细说明)。

另一个需要考虑的重要事项是方法签名的大小。如果你的方法有很多参数,这可能意味着你有糟糕的抽象。如果您将参数分组到集合对象中并将这些对象用作参数,则可以减少必须进行的参数检查的次数。您可以将参数检查移至这些对象,而不必在使用它们的每个函数中检查它们。

因此,与其将十个相关参数传递给每个函数,不如找出每个函数中使用的少数参数并将它们打包到一个对象中,并在该对象中包含验证参数的方法。如果需要更新有关一个参数的规则,这具有易于更改的附加优势。

于 2009-07-21T11:32:00.727 回答
5

对于我们中间的 C# 3.0 开发人员来说,封装这种 null 检查的好方法是在扩展方法中。

public void Foo(string arg1, int? arg2)
{
  arg1.ThrowOnNull();
  arg2.ThrowOnNull();
}

public static class extensions
{
    public static void ThrowOnNull<T>(this T argument) where T : class
    {
        if(argument == null) throw new ArgumentNullException();
    } 
}

如果你愿意,你总是可以重载它来获取参数名称。

于 2009-07-21T12:52:33.823 回答
3

Lou 的答案的一个小改进是改用哈希表,这意味着它不仅检查字符串,还检查对象。在方法中填充和处理也更好:

public static class ParameterChecker
{
    public static void CheckForNull(Hashtable parameters)
    {
        foreach (DictionaryEntry param in parameters)
        {
            if (param.Value == null || string.IsNullOrEmpty(param.Value as string))
            {
                throw new ArgumentNullException(param.Key.ToString());
            }
        }
    }
}

正如你会使用的那样:

public User CreateUser(string userName, string password, string Email, string emailAlerts, string channelDescription)    
{
    var parameters = new Hashtable();
    parameters.Add("Username", userName);
    parameters.Add("Password", password);
    parameters.Add("EmailAlerts", emailAlerts);
    parameters.Add("ChannelDescription", channelDescription);
    ParameterChecker.CheckForNull(parameters);

    // etc etc
}
于 2009-07-21T11:59:34.603 回答
2

我会坚持你原来的方法,只是传入参数名称。原因是,一旦您开始编写这些帮助程序,当每个人开始使用不同的约定来编写帮助程序时,就会成为一个问题。当有人查看您的代码时,他们现在必须检查以确保您在调试代码时正确编写了帮助程序。

继续分别检查每个参数,尽管您的手指因输入 Grasshopper 而感到疲倦 :) 当您的追随者收到意外的 ArgumentException 并从调试运行中保存以确定哪个参数失败时,他们会祝福您。

于 2009-07-21T12:31:17.123 回答
0

我给你的第一个建议是获取 ReSharper。它会告诉您何时存在可能的空值问题,何时不需要检查它们,并且只需单击鼠标即可添加检查。话说回来...

您不必检查不能为空的 int 或 bool。

可以使用 string.IsNullOrEmpty() 检查字符串...

如果您仍然决定要检查每个参数,您可以使用命令设计模式和反射,但是您的代码将不必要地笨重,或者使用以下内容并为每个方法调用它: private myType myMethod(string param1, int param2, byte[] param3) { CheckParameters("myMethod", {param1, param2, param3}); // 其余代码...

在你的实用程序类中放这个:

///<summary>Validates method parameters</summary>
///... rest of documentation
public void CheckParameters(string methodName, List<Object> parameterValues) 
{
    if ( string.IsNullOrEmpty(methodName) )
       throw new ArgumentException("Fire the programmer! Missing method name", "methodName"));

    Type t = typeof(MyClass);
    MethodInfo method = t.GetMethod(methodName);
    if ( method == null )
       throw new ArgumentException("Fire the programmer! Wrong method name", "methodName"));
    List<ParameterInfo> params = method.GetParameters();
    if ( params == null || params.Count != parameterValues.Count )
       throw new ArgumentException("Fire the programmer! Wrong list of parameters. Should have " + params.Count + " parameters", "parameterValues"));

    for (int i = 0; i < params.Count; i++ )
    {
            ParamInfo param = params[i];
            if ( param.Type != typeof(parameterValues[i]) )
                throw new ArgumentException("Fire the programmer! Wrong order of parameters. Error in param " + param.Name, "parameterValues"));
            if ( parameterValues[i] == null )
                throw new ArgumentException(param.Name + " cannot be null");
    }
} // enjoy
于 2009-07-21T12:02:08.387 回答
0

使用AggregateException带有多个ArgumentNullException实例列表的(意味着包含多个异常)。不要忘记也利用以下parameterName参数的ArgumentNullException优势nameof()

var exceptions = new List<Exceptions>();

if (firstArgument == null)
    exceptions.Add(new ArgumentNullException(nameof(firstArgument), "Some optional message"));

if (secondArgument == null)
    exceptions.Add(new ArgumentNullException(nameof(secondArgument), "Another optional message"));

if (exceptions.Count > 0)
    throw new AggregateException(exceptions);
于 2021-05-07T02:35:23.577 回答