【问题标题】:Nullability analysis fails to warn on null coming from NameValueCollection. Why?可空性分析无法对来自 NameValueCollection 的空值发出警告。为什么?
【发布时间】:2020-09-15 19:43:27
【问题描述】:

考虑一个在.csproj 中启用了可空性分析的.net core 项目:

<Project Sdk="Microsoft.NET.Sdk">

    <PropertyGroup>
        <TargetFramework>netcoreapp3.1</TargetFramework>
        <RootNamespace>(...)</RootNamespace>
        <IsPackable>false</IsPackable>
        <LangVersion>latest</LangVersion>
        <Nullable>enable</Nullable>
        <WarningsAsErrors>nullable</WarningsAsErrors>
    </PropertyGroup>
(...)

现在考虑这段代码:

public class NullabilityTests
{
    [Fact]
    public void Test()
    {
        var nameValueCollection = new NameValueCollection();
        string nullValue = nameValueCollection["I do not exist"];
        Foo(nullValue);
    }

    private void Foo(string shouldBeNotNull) => Assert.NotNull(shouldBeNotNull);
}

此代码编译并运行时没有任何警告,但在 Assert.NotNull 上测试失败。因此,可空性分析无法检测到空值,甚至无法警告可能空值。为什么?如果发生这种情况,我如何知道何时信任 C# 8 可空性分析?

【问题讨论】:

  • 你确定NameValueCollection 是可空注释的吗?
  • @PavelAnikhouski 我对可空性注释不够熟悉,不知道如何检查或如何应用。我猜一些 .NET 类型是由平台作者注释的,但有些不是。如果不存在注释,我能否以某种方式告诉 C# 始终将类型和成员视为可能为空的?我可以自己添加这些注释吗?
  • 无论如何,您应该使用更现代的集合。将字典用于键控对的集合,而不是 C# 1.0 中的非泛型版本。过时的类型不会得到更新。
  • 当前sources 告诉索引器返回string?,但在.net core 3.1 中我只看到string。我不知道如何注释 3rd 方库。你可以使用不同的集合,比如字典,已经注释了
  • 收藏来自HttpUtility.ParseQueryString。我不确定是否存在具有更现代返回类型的替代方案。

标签: c# .net-core nullable c#-8.0 nullable-reference-types


【解决方案1】:

NameValueCollection 类型还没有更新。来自Introducing Nullable Reference Types in C#

但是,如果他们在您之后添加注释,那么情况会更烦人。在他们这样做之前,您会“错误地”将他们的一些输入和输出解释为非空值。您将收到您不“应得”的警告,并错过您应该收到的警告。你可能不得不使用!在一些地方,因为你确实知道得更多。

(强调我的)

特别地,该类型被声明为返回类型string,当启用可空引用类型时,意味着该引用不能为空。编译器没有理由不这么想,所以不会发出警告。

既然您知道null 是一个可能的返回值,那么您可以强制执行它。您只需用正确的类型声明接收变量即可:

string? nullValue = (string?)nameValueCollection["I do not exist"];

(您需要显式强制转换,否则编译器的静态分析仍会将现在可为空的string? 变量视为非空。)

通常,解决此问题的理想方法是使用更现代的类型。例如,您可以将Dictionary&lt;string, string?&gt; 用作等效的可空感知集合。但是,您注意到,在这种情况下,您从另一个 .NET 方法 (HttpUtility.ParseQueryString()) 中获取集合。

如果您想要更强大的保护,您可以立即将集合复制到Dictionary&lt;string, string?&gt;,或者您可以实现NameValueCollection 类型的包装器,该包装器使用更正确的string? 返回类型声明索引器。

前者的一个可能的例子是这样的:

var nameValueDictionary = nameValueCollection
    .Cast<string>()
    .ToDictionary(k => k, k => (string?)nameValueCollection[k]);

【讨论】:

    【解决方案2】:

    你创建了一个字符串集合——字符串自然是可以为空的类型

    您查找一个不存在的项目,当您请求一个不存在的类型时,该类型将返回 null

    Caution
    
    The Get method does not distinguish between null which is returned because the specified key is not found and null which is returned because the value associated with the key is null.
    

    https://docs.microsoft.com/en-us/dotnet/api/system.collections.specialized.namevaluecollection?view=netcore-3.1

    【讨论】:

    • “字符串自然是可以为空的类型” -- false。当启用可空引用类型时,string不可为空。只有 string? 可以为空。这个答案没有回答问题。它无法解释为什么 NameValueCollection 索引器返回的值(显然可以返回 null 值)被允许分配给 non-nullable 类型的变量,即string
    • 很公平,未能正确吸收“在 .csproj 中启用可空性分析:”
    猜你喜欢
    • 2022-12-17
    • 1970-01-01
    • 2018-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-19
    相关资源
    最近更新 更多