检查空参数的最佳方法(Guard子句)

例如,您通常不希望构造函数中的参数为null,因此看到类似的东西是很正常的

if (someArg == null) { throw new ArgumentNullException(nameof(someArg)); } if (otherArg == null) { throw new ArgumentNullException(nameof(otherArg)); } 

它会使代码混乱一些。

有没有办法比这更好地检查一个参数列表的参数?

像“检查所有参数并抛出ArgumentNullException,如果它们中的任何一个为null并且为您提供null的参数”之类的东西。

顺便说一下,关于重复的问题声明,这不是关于用属性或内置的东西标记参数,而是有人称之为Guard Clauses以保证对象接收初始化的依赖关系。

 public static class Ensure { ///  /// Ensures that the specified argument is not null. ///  /// Name of the argument. /// The argument. [DebuggerStepThrough] [ContractAnnotation("halt <= argument:null")] public static void ArgumentNotNull(object argument, [InvokerParameterName] string argumentName) { if (argument == null) { throw new ArgumentNullException(argumentName); } } } 

用法:

 // C# < 6 public Constructor([NotNull] object foo) { Ensure.ArgumentNotNull(foo, "foo"); ... } // C# >= 6 public Constructor([NotNull] object bar) { Ensure.ArgumentNotNull(bar, nameof(bar)); ... } 

DebuggerStepThroughAttribute非常方便,以便在调试时(或者在发生exception后附加调试器时)进行激活时,我不会在ArgumentNotNull方法内部,而是在null引用actually发生的调用方法中。

我正在使用ReSharper合同注释 。

  • ContractAnnotationAttribute确保我永远不会拼错参数( "foo" ),并且如果我重命名foo符号,它也会自动重命名。
  • NotNullAttribute帮助ReSharper进行代码分析。 因此,如果我执行new Constructor(null)如果从ReSharper收到警告,则会导致exception。
  • 如果您不想直接注释代码,您也可以使用可以与库一起部署的外部XML文件执行相同的操作 ,并且用户可以在其ReShaprer中进行最佳引用。

如果你的构造函数中有太多参数,你最好修改它们,但这是另一个故事。

为了减少样板validation代码,许多人编写了这样的Guard实用程序类:

 public static class Guard { public static void ThrowIfNull(object argumentValue, string argumentName) { if (argumentValue == null) { throw new ArgumentNullException(argumentName); } } // other validation methods } 

(您可以添加Guard类可能需要的其他validation方法)。

因此,只需要一行代码来validation参数:

  private static void Foo(object obj) { Guard.ThrowIfNull(obj, "obj"); } 

空引用是你必须要防范的一种麻烦。 但是,他们不是唯一的一个。 问题比这更广泛,并且归结为:方法接受某种类型的实例,但它无法处理所有实例。

换句话说,该方法的域大于它处理的值集。 然后使用Guard子句断言实际参数不属于方法域的“灰色区域”,无法处理。

现在,我们有空引用,作为一个通常在可接受的值集之外的值。 另一方面,经常发生该集合的一些非空元素也是不可接受的(例如空字符串)。

在这种情况下,可能会发现方法签名过于宽泛,从而表明存在设计问题。 这可能导致重新设计,例如定义子类型,通常是派生接口,其限制方法的域并使一些保护子句消失。 您可以在本文中找到一个示例: 为什么我们需要保护条款?

有一个名为Swissknife的nuget包。 从nuget gallery安装Swissknife。 它为您提供了许多选项,从以下方法开始,检查参数Argument.IsNotNullOrEmpty(args,"args")下的Swissknife.Diagnostics.Contracts命名空间以及选项idoim等等。您可以设置Option _someVar然后检查是否_someVar.IsSome or _SomeVar.IsNone 。这也有助于打击可以为空的类。 希望这可以帮助。

您可以尝试使用“Heleonix.Guard”库来提供防护function: https : //github.com/Heleonix/Heleonix.Guard/blob/master/README.md您可以编写如下保护条款:

 // C# 7.2+: Non-Trailing named arguments Throw.ArgumentNullException(when: param.IsNull(), nameof(param)); // OR // Prior to C# 7.2: You can use a helper method 'When' Throw.ArgumentNullException(When(param.IsNull()), nameof(param)); // OR Throw.ArgumentNullException(param == null, nameof(param)); // OR Throw.ArgumentNullException(When (param == null), nameof(param)); 

它提供了许多现有exception,您可以为自定义exception编写自定义扩展方法。 此外,该库引用了具有预测扩展的’Heleonix.Extensions’库,如IsNullIsNullOrEmptyOrWhitespaceIsLessThan等等,以检查您的参数或变量与所需的值。 与其他具有流畅接口的其他保护库不同,这些扩展不会生成中间对象,并且由于实现非常简单,因此它们具有高性能。