退出构造函数时,不可为空的属性必须包含非空值。考虑将属性声明为可为空

Posted

技术标签:

【中文标题】退出构造函数时,不可为空的属性必须包含非空值。考虑将属性声明为可为空【英文标题】:Non-nullable property must contain a non-null value when exiting constructor. Consider declaring the property as nullable 【发布时间】:2021-08-02 22:01:18 【问题描述】:

我有一个像这样的简单课程。

public class Greeting

    public string From  get; set; 
    public string To  get; set;  
    public string Message  get; set; 

奇怪的是,我收到以下警告。

Severity    Code    Description Project File    Line    Suppression State
Warning CS8618  Non-nullable property 'From' must contain a non-null value when exiting constructor. 
Consider declaring the property as nullable.    MxWork.Elsa2Wf.Tuts.BasicActivities  
D:\work\MxWork\Elsa2.0WfLearning\MxWork.Elsa2.0Wf.Tuts\src 
\MxWork.Elsa2Wf.Tuts.BasicActivities\Messages\Greeting.cs   5   Active

我很困惑。它抛出的这些新类型的信息降低了我的信心。 我从所有三个属性中得到它们。 而这突然出现了。

有人可以建议如何减轻这种情况。

更新

这些天我看到像这样使用 default! 并且它的工作原理。

public class Greeting

    public string From  get; set;  = default!;
    public string To  get; set;  = default!;
    public string Message  get; set;  = default!;

如果你觉得合适的话,你也可以放一个问号(?)来表示该类型是可以为空的。

public class Greeting

    public string? From  get; set; ;
    public string? To  get; set; ;
    public string? Message  get; set; ;

【问题讨论】:

您已启用Nullable Reference Types。如果您没有预料到这一点,并且您不确定如何使用它们,最好将它们关闭(直到您有机会跟上进度) 这正是为了增加您对代码的信心而发出警告。没有它,以后更容易出现空引用错误。 关于您的更新,对于字符串和其他引用类型,default!null! 相同。我更喜欢null!,因为使用default 不会添加任何有用的信息,并且会误导人们认为null 之外的其他内容被设置在那里。 【参考方案1】:

如果您不想这样做,可以通过从 csproj 文件中删除以下行或将其设置为 disable 来禁用此功能。默认值为disable

<Nullable>enable</Nullable>

Here是官方文档。

【讨论】:

解决问题太好了 我已完全关闭 Visual Studio,编辑我的 csproj 文件以删除这些行,重新打开我的解决方案,但我仍然看到警告。 @RayGoudie 尝试清理并再次构建。您的解决方案中是否还有多个项目?如果是,那么您必须从所有项目中删除该行。 只有一个项目。清理和重建,以及删除 obj 目录并没有删除警告。 这感觉更像是一种创可贴,而不是真正的解决方案。我认为@slate 有最好的解决方案,对于较小的项目,我认为 Daryl Wagoner 的选择以及 Vivek 后来直接将属性注释为不可为空的建议都很棒。【参考方案2】:

编译器警告您,字符串属性的默认分配(为 null)与其声明的类型(非 null string)不匹配。

当nullable reference types 被打开时发出,这会将所有引用类型更改为非空,除非用? 另有说明。

例如,您的代码可以更改为

public class Greeting

    public string? From  get; set; 
    public string? To  get; set;  
    public string? Message  get; set; 

将属性声明为可为空的字符串,或者您可以在线或在构造函数中提供属性默认值:

public class Greeting

    public string From  get; set;  = string.Empty;
    public string To  get; set;  = string.Empty;
    public string Message  get; set;  = string.Empty;

如果您希望将属性的类型保留为非空。

【讨论】:

不可为空字符串的合理默认值通常是string.Empty Late-initialized properties, Data Transfer Objects, and nullability,介绍了在可空引用类型打开时迁移的 3 种策略、构造函数绑定、创建可空支持字段以及使用空宽恕运算符将属性初始化为空。跨度> Grant 链接的页面已更改,不再包含该部分。它在 Internet 档案中:Late-initialized properties, Data Transfer Objects, and nullability 但不是字符串string test; 默认测试为空 @variable 正确,如果您启用了 Nullable,则不允许这样做。【参考方案3】:

您可以将属性直接注释为不可为空。

public string Property get; set;  = null!;

如果用户尝试将Property 设置为null,它会发出警告

【讨论】:

我没有收到警告是我尝试将属性设置为空。 如果您指定null!;,您是在对编译器说“分配这个空值,但相信我,它不是空值”。见docs.microsoft.com/en-us/dotnet/csharp/language-reference/…【参考方案4】:

在运行应用程序时,启用可为空的引用类型将为您省去很多麻烦。许多警告的问题在于,大多数警告可能不会导致问题,但它可能会隐藏导致难以发现的错误的警告。

使用它有一些陷阱,就像问题指出的那样,Slate 和其他人回答得很好。

让警告尽可能接近于零是一个非常好的主意。

启用 nullable 后,它会产生很多警告。很多时候,编译器只知道某些东西可能为空。但是,您比编译器更聪明,您知道当它到达该代码时它不会为空。

例如:

    public partial class Exams: ComponentBase
    
    [Inject] private IQuestionPoolFetchService? QPoolService  get; init; 


        private async Task FetchQuestionPool()
        
            await QPoolService.GetAllQuestionsFromText();
        

这将引发 CS8602 警告。因为也许 DI 会以某种方式发送一个空值。当然,我们知道这不会发生。

您可以使用 #prama 来消除警告,例如:

    public partial class Exams: ComponentBase
    
    [Inject] private IQuestionPoolFetchService? QPoolService  get; init; 


        private async Task FetchQuestionPool()
        
#pragma warning disables CS8602 // Dereference of a possibly null reference.
            await QPoolService.GetAllQuestionsFromText();
#pragma warning restore CS8602 // Dereference of a possibly null reference.
        

这是非常难看的代码,如果你必须重复多次,就会变得更糟。

更好的解决方案: 使用容错运算符。 “!”

public partial class Exams: ComponentBase

[Inject] private IQuestionPoolFetchService? QPoolService  get; init; 


    private async Task FetchQuestionPool()
    
        await QPoolService!.GetAllQuestionsFromText();
        // null-forgiving ^
    

这告诉编译器嘿,我知道这可能为空,但它不会。

【讨论】:

有没有办法将属性本身注释为非空?否则,像 Hot Chocolate (GraphQL) 这样使用反射的库是无法知道的。【参考方案5】:

您还可以实现构造函数来消除错误。

public class Greeting

    public string From  get; set; 
    public string To  get; set; 
    public string Message  get; set; 

    public Greeting(string from, string to, string message)
    
        From = from;
        To = to;
        Message = message;
    

【讨论】:

【参考方案6】:

对于实体框架Working with nullable reference types:

public class NullableReferenceTypesContext : DbContext 
    public DbSet<Customer> Customers => Set<Customer>();
    public DbSet<Order> Orders => Set<Order>();

【讨论】:

以上是关于退出构造函数时,不可为空的属性必须包含非空值。考虑将属性声明为可为空的主要内容,如果未能解决你的问题,请参考以下文章

无法删除用户外部登录

必须初始化不可为空的实例字段“taskTitle”

sql查询不为空的字段

java如何判断非空

外键约束,带有子对象集合的 EF

Kotlin 中的非空值产生空指针异常