如何抑制可能的空引用警告

How to suppress Possible Null Reference warnings

我正在使用 c# 8 中的可空类型,我发现了一个困扰我的问题。 假设我有一个采用可为空参数的方法。当参数为空时,我想抛出一个特定的异常。但我希望该方法是干净的,并在其他地方检查参数。 check方法抛出异常,所以方法后的参数不能为null。 不幸的是,编译器没有看到这一点并向我发出警告。 方法如下:

    public void Foo(string? argument)
    {
        GuardAgainst.Null(argument, nameof(argument));
        string variable = argument; // <-- Warning CS8600  Converting null literal or possible null value to non - nullable type
        var length = argument.Length; //<--Warning CS8602  Dereference of a possibly null reference
    }

检查方法如下:

    public static void Null(string? text, string paramName)
    {
        if (text == null)
            throw new ArgumentNullException(paramName);
    }

现在,我可以这样抑制警告:

#pragma warning disable CS8602
var length = argument.Length;
#pragma warning restore CS8602

但这有点扼杀了我保持代码清洁的意图。 所以我的问题是:有没有更好的方法来抑制警告?或者告诉编译器从现在开始保证参数不为空?

好的,看起来有一个非常简单的解决方案 - the ! operator 守卫后必须使用一次,然后才认为不为空:

public void Foo(string? argument)
{
    GuardAgainst.Null(argument, nameof(argument));
    var length = argument!.Length; 
}

这就是你想要的:

public static void Null<T>([NotNull] T? value, string paramName)
{
    if (value == null)
        throw new ArgumentNullException(paramName);
}

[NotNull]属性指示分析调用此方法后,value不会null.

这意味着您不需要 ! 运算符,这样更加简洁自然。

void M(string? argument)
{
    GuardAgainst.Null(argument, nameof(argument));
    string variable = argument; // no warning
    // ...
}

此处使用不受约束的泛型类型参数T意味着此方法适用于引用类型(例如string)和可空值类型(例如int?) .

如果您使用的是 .NET 6,您可以通过 CallerArgumentExpressionAttribute 进一步简化它,如下所示:

public static void Null<T>(
    [NotNull] T? value,
    [CallerArgumentExpression(parameterName: "value")] string? paramName = null)
{
    if (value == null)
        throw new ArgumentNullException(paramName);
}

这样,第二个参数可以省略,调用者可以简化为:

GuardAgainst.Null(argument);

将类型上的 ? 说明符视为两件事:1) 调用前该值可以为 null,以及 2) 调用后该值可以为 null。另一种写法是[AllowNull, MaybeNull]。在可空上下文中缺少 ? 同样意味着 [DisallowNull, NotNull]。对于您的 Null 方法,由于 NotNull.

的手动规范,我们最终得到 [AllowNull, NotNull]

考虑使用 null 合并运算符的解决方案 ??

The null-coalescing operator ?? returns the value of its left-hand operand if it isn't null; otherwise, it evaluates the right-hand operand and returns its result. The ?? operator doesn't evaluate its right-hand operand if the left-hand operand evaluates to non-null.

public void Foo(string? argument)
{
    string variable = argument ?? throw new ArgumentNullException(nameof(argument));
    var length = argument.Length;
}

我认为此解决方案更简洁。您避免检查 GuardAgainst class 和 .Null() 静态方法实现细节。