为什么 Intellicode '... is not null' 当它明显为空时?

som*_*guy 1 c# nullable-reference-types

编辑:

感谢 canton7,我将研究可空引用类型,因为这是此处显示的 C# 8 功能。

编辑2:

TL;DR:消息“'_products' 此处不为空”。纯粹基于变量_products定义。如果它被定义为可空引用类型,它会说“这里可能为空”,如果它被定义为非可空引用类型(=正常,基本定义,不带“?”),它会说,显示在下图。这两种情况都只是提示/警告,不会改变编译后的代码。

原问题:

调用方法:

protected override async Task OnInitializedAsync() //ignore the Polymorphism
{
    _products = await ProdService.GetProductListAsync();
    Console.WriteLine("_products: " + _products + " | is null?: " + (_products == null));
//output: <<_products:   is null? True>>
}
Run Code Online (Sandbox Code Playgroud)

图像:

在此输入图像描述

调用方法:

public async Task<List<Product>> GetProductListAsync()
{
    return null;
}
Run Code Online (Sandbox Code Playgroud)

图像:

在此输入图像描述

can*_*on7 13

您已启用 C# 8 功能“可为空引用类型”(NRT)。这添加了一层静态分析,它会告诉您引用类型何时可能为空,并在您取消引用可能为空的内容时发出警告。请参阅此文档此 MSDN 博客文章

您可以通过<Nullable>disable</Nullable>在 .csproj 中进行设置来禁用 NRT。

签名Task<List<Product>> GetProductListAsync()承诺此方法将返回一个 non-null Task,其中包含一个 non-nullList的非null Products

任何调用此方法的代码都只能看到签名。编译器相信您正确编写了签名。由于签名表明此方法不返回 null,因此这意味着_products显示为“not null”。

(想想如果这个方法是在不同的 DLL 中定义的,会发生什么。编译器没有可用的源代码来分析它。这种全程序类型推断也是非常昂贵和脆弱的:最好具有程序员明确声明的接口,而不是推断的接口。)

但是,您在方法的实现中违反了这一承诺,您确实返回了 null。就是编译器对你抱怨的地方。最终屏幕截图中的 CS8603 显示编译器抱怨,因为方法签名承诺您不会返回 null,但您却有return null.

如果该方法确实可以返回 null,那么它的签名应该是:

Task<List<Product>?> GetProductListAsync()
Run Code Online (Sandbox Code Playgroud)

这将使警告消失return null,并_products显示为“也许为空”。

  • @somedotnetguy 首先决定是否要使用 NRT。它们带来了很多好处,但还有一些学习要做。如果您确实启用了它们,那么“Foo?”意味着“Foo”可能为空,也可能不为空,而“Foo”则意味着不会为空。这适用于局部变量、方法参数、返回类型、字段、属性等。值得从文档开始并从头开始学习 NRT。 (2认同)
  • @somedotnetguy 是的,在几十年的“任何引用类型都可以为空”之后引入这种静态分析本质上是有风险的。NRT 的设计理念是:1)它们只提供信息;它们不会破坏您的代码,并且 2) 它们对生成的代码没有影响:启用 NRT 永远不会改变编译器生成的内容。您始终可以使用“TreatWarningsAsErrors”将这些特定警告升级为错误,在 .editorconfig 中手动将 CS8603 等设置为错误严重性。这也意味着他们可以添加新的静态分析功能,而无需进行重大更改 (2认同)