【问题标题】:How to tell C# that a struct's non-nullable fields may actually be null for nullability-analysis purposes?出于可空性分析的目的,如何告诉 C# 结构的不可为空字段实际上可能为空?
【发布时间】:2022-07-03 11:12:30
【问题描述】:

这是一个启用了可为空引用类型的示例 C# 程序:

using System;
using System.Collections.Generic;
using System.Linq;

MyStruct myStruct = new("A");
List<MyStruct> list = new() { myStruct };
MyStruct found = list.FirstOrDefault(item => item.Str == "B");
Console.WriteLine(found.Str.Length);

struct MyStruct
{
    public readonly string Str;

    public MyStruct(string str)
    {
        Str = str;
    }
}

注意MyStruct 包含一个不可为空 Str 字段。 理论上这意味着Str 字段永远不应该为空,并且编译器在几乎所有情况下都会警告你如果你让它为空。

然而,空值可能会出现的一种情况是,如果通过通用方法返回未初始化的结构,例如上面的 FirstOrDefault 调用。在这种情况下,Str 字段 为 null,但 C# 编译器在访问 Str 或分配 found 变量时不会给出警告,因此程序会因NullReferenceException 尝试访问 found.Str.Length 时。 (另一种情况,如果从数组中读取结构。)

更糟糕的是,一些代码分析工具会错误地警告反对检查以确保found.Str 不为空。 (例如,如果我添加 if(found.Str != null),那么 Resharper 将报告为“表达式始终为真”并提示将其删除,即使在这种情况下它绝对不是真的。)

在 C# 的可空性分析中,这似乎是一个足够大的“漏洞”,我不得不怀疑我是否遗漏了一些关于如何让编译器理解这种情况的内容。有没有办法“告诉”编译器 found 结构的字段可能为空,即使它们被声明为不可为空?

编辑:澄清一下,我知道this articlethis question 的答案,它们解释了为什么会发生这种情况。但我感兴趣的是如何处理它。具体来说,有没有办法告诉编译器某个实例字段可能为空,即使它被标记为不可为空,而无需将该字段的实际声明更改为可空。类似于您如何使用! 后缀表达式来告诉编译器,“相信我,即使它被标记为可空,这也不是空的”,我希望做相反的事情并说“相信我,这可能为 null,即使它被标记为不可为空”。 (如果有办法对结构实例的所有字段自动执行此操作,则可以加分,但我怀疑这是否可能。)

【问题讨论】:

标签: c# struct nullable-reference-types c#-10.0


【解决方案1】:

! (null-forgiving) operator 的逆属性是 [MaybeNull] 属性。

要使此属性在 .netstandard2.0 中起作用,您可以将 NullabilityAttributes.cs 的副本放入您的项目中:

#if !NETCOREAPP3_0_OR_GREATER && !NETSTANDARD2_1_OR_GREATER && !NET5_0_OR_GREATER
namespace System.Diagnostics.CodeAnalysis{
   // copy of `NullableAttributes.cs` goes here:
   // https://source.dot.net/#System.Private.CoreLib/NullableAttributes.cs,68093cc4b5713519
}
#endif

这将导致 ReSharper 等静态分析工具正确处理该值,就好像它具有 ?

我不知道这是否/如何影响.NET 6 NullabilityInfo APIs,但对于像 ReSharper 这样的静态分析工具,[MaybeNull] string 的操作似乎与string? 相同;例如,当用作带有notnull constraint 的泛型类型参数时,它会导致相同的警告。

【讨论】:

    猜你喜欢
    • 2020-03-19
    • 2022-01-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-30
    • 2014-07-11
    • 2021-12-09
    相关资源
    最近更新 更多