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

Sup*_*JMN 20 c# constructor arguments

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

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

if (otherArg == null)
{
    throw new ArgumentNullException(nameof(otherArg));
}
Run Code Online (Sandbox Code Playgroud)

它会使代码混乱.

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

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

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

man*_*c66 36

使用较新版本的 C# 语言,您无需额外的库或额外的方法调用即可编写此代码:

_ = someArg ?? throw new ArgumentNullException(nameof(someArg));
_ = otherArg ?? throw new ArgumentNullException(nameof(otherArg));
Run Code Online (Sandbox Code Playgroud)

  • 如果您还没有遇到过[discards](https://docs.microsoft.com/en-us/dotnet/csharp/discards)并且像我一样想知道下划线。 (9认同)
  • 即将推出的 .NET6 的更新更短方法:`ArgumentNullException.ThrowIfNull(someArg);` (/sf/answers/4888541031/) (2认同)

bit*_*onk 20

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

用法:

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

// C# >= 6
public Constructor([NotNull] object bar)
{
    Ensure.ArgumentNotNull(bar, nameof(bar));
    ...
}
Run Code Online (Sandbox Code Playgroud)

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

我正在使用ReSharper Contract Annotations.

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


ser*_*nka 13

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

为了减少样板验证代码,许多人编写了Guard实用程序类,如下所示:

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

    // other validation methods
}
Run Code Online (Sandbox Code Playgroud)

(您可以添加Guard类可能需要的其他验证方法).

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

    private static void Foo(object obj)
    {
        Guard.ThrowIfNull(obj, "obj");
    }
Run Code Online (Sandbox Code Playgroud)

  • 只是一个小注释,您也可以使用`nameof`运算符而不是硬编码参数名称,例如`Guard.ThrowIfNull(obj,nameof(obj))` (12认同)

Ali*_*ned 7

Ardalis 有一个优秀的GuardClauses库。

用起来很好Guard.Against.Null(message, nameof(message));


use*_*856 7

使用较新版本的 C#(C# 10、.NET6 将在几天后发布),您甚至可以执行以下操作: ArgumentNullException.ThrowIfNull(someArg);

文档: https: //learn.microsoft.com/en-us/dotnet/api/system.argumentnullexception.throwifnull? view=net-6.0


Zor*_*vat 6

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

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

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

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


Jin*_*lye 5

在 C# 8.0 及更高版本中,提供了新的帮助。C# 8.0 引入了不可为 null 的引用类型(该功能在文档中被称为“可为 null 的引用类型”,有些令人困惑)。在 C# 8.0 之前,所有引用类型都可以设置为 null。但现在使用 C# 8.0 和“可为空”项目设置,我们可以说引用类型默认为不可为空,然后根据具体情况使变量和参数可为空。

因此,目前我们识别的代码如下:

public void MyFunction(int thisCannotBeNull, int? thisCouldBeNull)
{
    // no need for checking my thisCannotBeNull parameter for null here
}
Run Code Online (Sandbox Code Playgroud)

如果您<Nullable>enable</Nullable>为 C# v8.0+ 项目进行设置,您也可以执行以下操作:

public void MyFunction(MyClass thisCannotBeNull, MyClass? thisCouldBeNull)
{
    // static code analysis at compile time checks to see if thisCannotBeNull could be null
}
Run Code Online (Sandbox Code Playgroud)

空值检查是在编译时使用静态代码分析完成的。因此,如果您的编码方式意味着可能会出现空值,您将收到编译器警告(如果需要,您可以将其升级为错误)。因此,许多(但不是全部)需要对空参数进行运行时检查的情况都可以根据您的代码作为编译时检查进行处理。


nds*_*svw 5

运算!!符(实际上不是 C# 的一部分,但有趣的故事)

Microsoft 尝试引入一种新的语言功能,该功能在 C# 10 中称为参数 null 检查bang bang 运算符,后来又在 C# 11 中引入,但决定不发布它

这将是执行此操作的最短方法(到目前为止):!!在要检查 null 的参数后面只有 2 个感叹号。

前:

public void test(string someArg){
    if (someArg == null)
    {
        throw new ArgumentNullException(nameof(someArg));
    }
    // other code
}
Run Code Online (Sandbox Code Playgroud)

使用新的运算符:

public void test(string someArg!!){
    // other code
}
Run Code Online (Sandbox Code Playgroud)

调用test(null)将导致 ArgumentNullException 告诉您这someArgnull

微软在 2021 年初提到过它,但它并没有成为 C# 10 的一部分:“Microsoft Developer”YouTube 频道上的视频解释了新功能

该功能于 2022 年 2 月针对 C#11 实现,请参阅 GitHub

后来在微软开发者博客上看到它被删除了,因为有很多批评,认为bang bang 操作员非常吵闹

C# 语言的开发人员认为,大多数人都支持这样的功能,但它不像平常那​​么大,并且有可能该功能会随更高版本的 C# 一起提供