【问题标题】:Can I tell C# nullable references that a method is effectively a null check on a field我可以告诉 C# 可空引用方法实际上是对字段的空检查吗
【发布时间】:2020-03-19 23:07:53
【问题描述】:

考虑以下代码:

#nullable enable
class Foo
{
    public string? Name { get; set; }
    public bool HasName => Name != null;
    public void NameToUpperCase()
    {
        if (HasName)
        {
            Name = Name.ToUpper();
        }
    }
}

在 Name=Name.ToUpper() 上,我收到一条警告,指出 Name 可能是空引用,这显然是不正确的。我可以通过内联 HasName 来解决此警告,因此条件是 if (Name != null)。

有什么方法可以指示编译器 HasName 的真实响应意味着对 Name 的不可为空性约束?

这很重要,因为 HasName 实际上可能会测试更多的东西,我可能想在多个地方使用它,或者它可能是 API 表面的公共部分。有很多理由希望将 null 检查因素考虑到它自己的方法中,但这样做似乎会破坏可空引用检查器。

【问题讨论】:

  • IMO 你应该在可空类型上使用HasValue,而不是对照null。不过,它可能不会影响您的问题。
  • 我认为对于您的情况,您可以使用#nullable disable 包装您的代码,然后再使用#nullable enablerestore (docs.microsoft.com/en-us/dotnet/csharp/…)。
  • 您可以使用“该死”! 运算符。 if(HasName) { Name = Name!.ToUpper(); }
  • 对于多线程应用程序,您可以在 HasName 检查后让 Name 为 null,在本地使用变量而不是返回到属性(谁知道该属性在其 getter 中可能会做什么)是会给出一些时髦的错误(请记住使用事件处理程序,这种情况经常发生)

标签: c# nullable-reference-types


【解决方案1】:

更新:

C# 9.0 以MemberNotNullWhenAttribute 的形式引入了您要查找的内容。在您的情况下,您想要:

#nullable enable
class Foo
{
    public string? Name { get; set; }

    [MemberNotNullWhen(true, nameof(Name))]
    public bool HasName => Name != null;
  
    public void NameToUpperCase()
    {
        if (HasName)
        {
            Name = Name.ToUpper();
        }
    }
}

还有MemberNotNullAttribute 用于无条件断言。

旧答案:

我环顾了System.Diagnostics.CodeAnalysis 的不同属性,找不到任何适用的东西,这非常令人失望。最接近您想要的似乎是:

public bool TryGetName([NotNullWhen(true)] out string? name)
{
    name = Name;
    return name != null;
}

public void NameToUpperCase()
{
    if (TryGetName(out var name))
    {
        Name = name.ToUpper();
    }
}

我知道,它看起来很麻烦。你可以看看the MSDN docs for nullable attributes,说不定你会发现更整洁的东西。

【讨论】:

  • 似乎我们需要更多属性或类似打字稿的断言
  • 我会选择这个作为答案,因为我担心的真正答案似乎是“不,c# 还没有这样做。”
  • @JohnMelville 我也找不到此类功能的提案,所以我认为我们不会很快期待这种变化。
  • @XIU 编译器在这方面已经松懈了。如果您执行if(Name != null) return Null.ToUpper(),则不会出现空取消引用的警告,即使从技术上讲它是一个 TOCTOU 竞争条件。我记得 Mads Torgersen 谈到他们是如何考虑这一点的,但它会产生如此多的误报,整个可空引用类型功能实际上将毫无用处 - 99% 的时间你的属性不会被另一个线程更改。因此,您需要做的就是创建一个属性,使对该属性的检查被视为对另一个属性的 null 检查。
  • 我修复了“找不到这个建议”的问题。 (github.com/dotnet/csharplang/issues/2997) 祝我好运。
【解决方案2】:

在 C# 9.0 中检查 [MemberNotNull(nameof(Property))][MemberNotNullWhen(true, nameof(Property))] 属性。

https://github.com/dotnet/runtime/issues/31877

【讨论】:

    【解决方案3】:

    String 是引用类型,可为 null(例如 int?)是可为 null 的值类型。所以你真的不能这样做string? myString;你需要的是这样的:

    class Foo
    {
        public string Name { get; set; }
        public bool HasName => !String.IsNullOrEmpty(Name);  ////assume you want empty to be treated same way as null
        public void NameToUpperCase()
        {
            if (HasName)
            {
                Name = Name.ToUpper();
            }
        }
    }
    

    【讨论】:

    猜你喜欢
    • 2022-07-03
    • 2021-05-30
    • 2014-11-14
    • 1970-01-01
    • 1970-01-01
    • 2017-02-26
    • 2020-10-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多