我可以告诉 C# 可空引用方法实际上是对字段的空检查吗

Posted

技术标签:

【中文标题】我可以告诉 C# 可空引用方法实际上是对字段的空检查吗【英文标题】:Can I tell C# nullable references that a method is effectively a null check on a field 【发布时间】: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 中可能会做什么)是会给出一些时髦的错误(请记住使用事件处理程序,这种情况经常发生) 【参考方案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();
        
    

【讨论】:

你已经过时了docs.microsoft.com/en-us/dotnet/csharp/language-reference/… @AluanHaddad 我相信你的意思是发送这个而不是docs.microsoft.com/en-us/dotnet/csharp/nullable-references

以上是关于我可以告诉 C# 可空引用方法实际上是对字段的空检查吗的主要内容,如果未能解决你的问题,请参考以下文章

C# 8中的可空引用类型

可空引用类型意外 CS8629 可空值类型可能为带有临时变量的空

有没有一种方便的方法来过滤一系列 C# 8.0 可空引用,只保留非空值?

使用 C# 8 的可空引用类型

Django模型字段实际上是对相关模型中的字段的引用

可空类型的 datagridview 组合框下拉列表中的空值