如果一个参数为null,最佳做法是什么?

时间:2009-07-21 11:23:48

标签: c# .net argument-validation

在验证方法的输入时,我常常检查参数是否为null,如果是,则抛出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:我想改变它,因为使用长方法,这样做非常繁琐。
想法?

8 个答案:

答案 0 :(得分: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");

答案 1 :(得分: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.
}

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

答案 2 :(得分:9)

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

考虑来自design by contract的原则,特别是你的功能的先决条件是什么,并规范一种强制执行它们的方法(Matt和Lou都在他们的答案中提出建议,所以我不需要详细说明)。

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

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

答案 3 :(得分: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();
    } 
}

如果你想要,你总是可以重载它以获取参数名称。

答案 4 :(得分: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
}

答案 5 :(得分:2)

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

分别检查每个参数,尽管你的手指因输入Grasshopper而感到厌倦:)你的粉丝会在遇到意外的ArgumentException时保佑你,并从调试运行中保存以确定哪个参数失败。

答案 6 :(得分:0)

使用带有多个 AggregateException 实例列表的 ArgumentNullException(用于包含多个异常)。别忘了还利用 parameterNameArgumentNullException 参数,它与 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);

答案 7 :(得分:-1)

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

除非您从某些旧版源接收信息,否则通常不必检查空字符串和整数。

可以使用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