带有支持字段的必需 init 属性给出 null 警告 C# 11

Joa*_*oas 6 .net c# c#-11.0

具有设置支持字段的必需 init 属性仍然会给出空警告。

下面的代码给出了一个警告:

警告 CS8618 退出构造函数时,不可空字段“_name”必须包含非空值。考虑将该字段声明为可为空。

public class TestRequiredInit
{
    private readonly string _name;

    public required string Name
    {
        get => _name;
        init => _name = value;
    }
}
Run Code Online (Sandbox Code Playgroud)

字段可为空警告

我没有看到一种TestRequiredInit在不_name设置为非空值的情况下创建的方法。 尝试创建都会给出警告或错误的类的不同方法

这是 MSBuild / VS 中的错误,还是我遗漏了什么?

更新以防止人们推荐使用汽车属性

为了提出问题,我对上面的代码进行了一些简化。我希望能够向属性初始值设定项添加初始化逻辑。

public class TestRequiredInit
{
    private readonly string _name;

    public required string Name
    {
        get => _name;
        init
        {
            if (value.Length > 50)
            {
                throw new ArgumentException();
            }

            _name = value;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Jer*_*ert 4

属性与其支持字段之间没有正式的链接(如果它们有的话,这不是必需的),因此_name必须单独分析 的可为空性Name。更重要的是,虽然initsetter 被认为是作为构造阶段的一部分发生的(因此它们可以执行诸如初始化其他init属性之类的事情),但它们并不是出于可空性分析目的而正式构造的一部分 - 在引入 之前这是正确的required,因为没有要求给他们打电话。

可空性分析可以扩展到专门考虑属性init的 setterrequired作为分析目的的构造的一部分,因此Name initsetter 将被视为_name处于已知的非 null 状态,但是正确实现这一点并不简单 - 例如,因为我们不能假设init调用 setter 的任何顺序,我们应该假设在每个setter内部,如果构造函数没有明确将字段分配为非 null,则字段可能是这样,这样我们就可以给出适当的警告。这是当前语义的反转,其中允许 setter假设字段 not ,因为它们当前可以依赖已完成此操作的构造函数,并受到现有警告的限制。如果属性的 setter 不是 setter,事情会变得更加棘手因为它可能在构造期间和之后被调用 - 我们是否应该始终发出警告以确保安全?initnullnullrequiredinit

所有这些,我什至没有考虑它如何与派生类交互。我肯定会同意“这可能被认为是一个错误,但正式定义所需的语义并尽可能少地实现它们是一项相当大的工作,但尚未进行”。

  • 微软根据我在他们的 github 中创建的讨论创建了一个语言提案:https://github.com/dotnet/csharplang/issues/6754 他们会让分析器尊重 `MemberNotNullAttribute` (2认同)