提取方法中的空验证

Null validation in extracted method

我在 .csproj

中使用 c# 8 和可空分析器
<PropertyGroup>
  <Nullable>enable</Nullable>
  <RunAnalyzersDuringBuild>true</RunAnalyzersDuringBuild>
  <RunAnalyzersDuringLiveAnalysis>true</RunAnalyzersDuringLiveAnalysis>
</PropertyGroup>

下面通过提取方法验证请求的代码显示了这样的警告:

class Program
{
    public void Foo(Data request)
    {
        Validate(request);

        request.Properties.TryGetValue("bar", out var bar);
    }

    private static void Validate(Data request)
    {
        if (request == null)
        {
            throw new ArgumentNullException(nameof(request));
        }

        if (request.Properties == null)
        {
            throw new ArgumentNullException(nameof(Data.Properties));
        }
    }
}

public class Data
{
    public Dictionary<string, string>? Properties { get; set; }
}

如果我将验证代码直接放入方法中(如下所示),则不会出现警告。

这是为什么?

如何提取验证并且没有警告?

class Program
{
    public void Foo(Data request)
    {
        if (request == null)
        {
            throw new ArgumentNullException(nameof(request));
        }

        if (request.Properties == null)
        {
            throw new ArgumentNullException(nameof(Data.Properties));
        }

        request.Properties.TryGetValue("bar", out var bar);
    }
}

public class Data
{
    public Dictionary<string, string>? Properties { get; set; }
}

我相信目前(如果你想在方法 Validation 中保持对 requestrequest.Properties 的验证)解决这个问题的唯一方法是使用 null forgiving operator:

request.Properties!.TryGetValue("bar", out var bar);

如果您考虑从方法 Validation 中提取 request.Properties 的验证,那么您可以使用属性 NotNullAttribute 来解决问题。属性 NotNullAttribute 指定在调用 returns 时输入参数不是 null。使用此属性,我们可以通过以下方式声明 Validate

private static void Validate([System.Diagnostics.CodeAnalysis.NotNull] object? obj, string name)
{
    if (obj == null)
    {
        throw new ArgumentNullException(name);
    }
}

调用此方法后 Validate 代码分析器知道 obj 不为空,并且不会对访问 obj.

的代码产生警告

然后我们可以使用 Validate 而不会收到警告:

public static void Foo(Data request)
{
    Validate(request, nameof(request));
    Validate(request.Properties, nameof(request.Properties));

    request.Properties.TryGetValue("bar", out var bar);
}