在基类构造函数调用的虚重写方法中使用[MemberNotNull]仍报警告的解决办法
问题场景
我有如下C#代码:
using System; using System.Diagnostics.CodeAnalysis; public class Base{ protected Base(){ Initialize(); } protected virtual void Initialize(){} } public class Derived : Base{ public string _someString; public Derived() : base(){ } [MemberNotNull(nameof(_someString))] protected override void Initialize(){ _someString = "Test"; } } public class Program { public static void Main() { Console.WriteLine("Hello World"); } }
运行后收到警告:
/home/Program.cs(16,9): warning CS8618: Non-nullable field '_someString' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. [/home/home.csproj]
虽然_someString会被基类构造函数触发的Initialize虚方法完成初始化,但编译器无法追踪这一逻辑。且Derived是自动生成的partial类,我希望避免在它的构造函数中手动调用Initialize。
解决方案
方案1:给派生类构造函数添加[MemberNotNull]属性
编译器无法自动追踪基类构造函数调用虚方法的流程,因此可以在派生类的构造函数上显式标注[MemberNotNull],明确告知编译器该构造函数会确保目标字段非空。
由于Derived是partial类,你可以在手动编写的partial部分中补充构造函数的属性声明,无需修改自动生成的代码:
using System.Diagnostics.CodeAnalysis; public partial class Derived { // 重复构造函数声明并添加属性 [MemberNotNull(nameof(_someString))] public Derived() : base() { } [MemberNotNull(nameof(_someString))] protected override void Initialize() { _someString = "Test"; } }
这样编译器会认可构造函数完成了字段的初始化,警告会自动消失。
方案2:使用null原谅运算符(简单但不推荐)
如果不想修改构造函数,可以在字段声明时用!运算符直接抑制警告,强制告知编译器该字段不会为null:
public string _someString = null!;
这种方法只是消除警告,并没有让编译器真正理解初始化逻辑,仅适用于你能100%确认字段一定会被初始化的场景。
方案3:修改基类为抽象类(若可行)
如果基类不需要被直接实例化,可以将Initialize设为抽象方法,这样编译器会明确知道派生类必须实现该方法,且基类构造函数调用时一定会执行派生类的初始化逻辑:
public abstract class Base{ protected Base(){ Initialize(); } protected abstract void Initialize(); }
该方法需要修改基类,仅适用于基类无需独立实例化的场景。
内容的提问来源于stack exchange,提问作者infinitezero

