如何正确修复此代码以支持可空引用而不出现警告

How to properly fix this code to support nullable references without warnings

使用以下项目文件设置:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net5.0</TargetFrameworks>
    <LangVersion>9.0</LangVersion>
    <Nullable>enable</Nullable>
    <EnableNETAnalyzers>true</EnableNETAnalyzers>
    <AnalysisLevel>latest</AnalysisLevel>
    <EnableNETAnalyzers>true</EnableNETAnalyzers>
    <AnalysisMode>AllEnabledByDefault</AnalysisMode>
  </PropertyGroup>
</Project>

使用以下代码:

using System;

namespace TestArrayValueHashCodeNullability
{
    public static class Test
    {
        /// <summary>
        /// Gets a hash code for the array based on the values in the array.
        /// </summary>
        /// <typeparam name="TYPE">The type of item in the array.</typeparam>
        /// <param name="array">The array to get the value hash code for.</param>
        /// <returns>A hash code based on the values in the array.</returns>
        public static int ValueHashCode<TYPE>(this TYPE[] array)
        {
            if (array == null) throw new ArgumentNullException(nameof(array));
            int code = array.Length;
            // loop through each element and compare
            for (int offset = 0; offset < array.Length; ++offset)
            {
                int elemhashcode;
                elemhashcode = array[offset]?.GetHashCode() ?? 0;   // either this line should work
                elemhashcode = array[offset].GetHashCode();         // or this one should
                code ^= (elemhashcode >> (32 - (offset % 32)) ^ (elemhashcode << (offset % 32)) ^ 0x1A7FCA3B);
            }
            return code;
        }
    }
}

我在另外两个 elemhashcode = 行中收到以下警告,它们似乎相互直接冲突。我相信其中一行应该在没有警告的情况下编译,因为 array[offset] 在该行上可能为 null 或者它不是——它不能同时为“可能为 null”和“从不为 null”。

Test.cs(21,32,21,45): warning CA1508: 'array[offset]' is never 'null'. Remove or refactor the condition(s) to avoid dead code.
Test.cs(21,32,21,60): warning CA1508: 'array[offset]?.GetHashCode()' is never 'null'. Remove or refactor the condition(s) to avoid dead code.
Test.cs(22,32,22,45): warning CS8602: Dereference of a possibly null reference.

我可以使用以下代码让警告消失:

                elemhashcode = array[offset]!.GetHashCode();

但这不应该是必要的。一个或另一个在技术上应该是正确的。这是编译器错误,还是我遗漏了一些微妙的解决方案?

在我看来,这似乎是代码分析功能与编译器本身之间的差异。

鉴于您发布的代码,第 22 行的错误是关键错误。您没有做任何事情来约束类型参数,因此确实有可能使用可为 null 的引用类型,因此您正在取消引用潜在的 null 引用。

第 21 行的警告在我看来是错误的。但是,请注意,只有当您在项目文件中包含 <AnalysisMode>AllEnabledByDefault</AnalysisMode> 时,它们才会出现。的确,似乎 there are known false-positive results for CA1508:

mavasani commented on Mar 28, 2019

This analyzer has known false positives and hence is turned off by default. I would recommend you remove the ruleset entry that is turning on this rule. When the false positives are fixed, we will enable this rule by default.

哪个版本的代码是正确的不取决于您收到的警告,而是取决于您对代码的意图 实际是什么。如果您希望用于类型参数的类型始终不可为空,则应将约束 where TYPE : notnull 添加到方法声明中,并保留 elemhashcode = array[offset].GetHashCode(); 语句。通过反映您对泛型方法的预期用途的适当约束,该警告将消失。

另一方面,如果您希望 TYPE 类型参数允许可空类型,则需要像第一条语句(即 elemhashcode = array[offset]?.GetHashCode() ?? 0; 中那样防止出现空值。在这种情况下,启用所有规则将导致已知的误报。

在那种情况下,如果您觉得您确实需要为项目的代码分析设置保留 AllEnabledByDefault,您可以通过多种方式抑制该特定警告,例如 #pragma代码、源代码或程序集级别 [SuppressMessage] 属性或代码分析配置文件。有关详细信息,请参阅 How to suppress code analysis warnings