C#10可空模式下如何告知编译器我在构造函数中间接设置了非空属性
解决方案
针对C#可空引用类型场景下,间接赋值触发的CS8618警告,有三种优雅的处理方案,适配不同版本和业务需求:
方案1:使用[MemberNotNull]特性(C# 9.0+ 推荐)
该特性是.NET官方为间接赋值场景专门设计的,用于标注方法执行后一定会为指定成员赋予非空值,编译器识别后就不会再抛出对应警告,不需要全局或局部抑制告警。
第一个示例修改后代码:
using System.Diagnostics.CodeAnalysis; class Test { string S { get; set; } public Test() { Init(); } [MemberNotNull(nameof(S))] private void Init() { S = "hello"; } }
第二个生产场景修改后代码:
由于S的setter执行时一定会为D赋值,给setter加上对应特性即可:
using System.Diagnostics.CodeAnalysis; class Test { public string S { get => _S; [MemberNotNull(nameof(D))] set => D = new Derived(_S = value); } public Derived D { get; private set; } public Test(string s) => D = new Derived(_S = s); private string _S; }
方案2:null!兜底赋值(C# 8.0 兼容方案)
如果你使用的C#版本低于9.0,无法使用[MemberNotNull]特性,可以在声明非空成员时直接赋值为null!,相当于主动向编译器担保该成员会在后续逻辑中被赋予非空值,可读性远高于直接抑制警告。
第一个示例修改后代码:
class Test { string S { get; set; } = null!; public Test() { Init(); } private void Init() { S = "hello"; } }
第二个生产场景修改后代码:
class Test { public string S { get => _S; set => D = new Derived(_S = value); } public Derived D { get; private set; } = null!; public Test(string s) => D = new Derived(_S = s); private string _S = null!; }
方案3:构造函数直接赋值(适配简单场景)
如果业务逻辑没有强制要求拆分初始化方法,可以把赋值逻辑直接迁移到构造函数内,从根源上消除编译器的识别歧义,该方案仅适合逻辑简单、无需复用初始化逻辑的场景。
内容的提问来源于stack exchange,提问作者Harry
相关产品推荐
相关产品推荐

