如何正确修复此代码以支持可空引用而不出现警告
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。
使用以下项目文件设置:
<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。