如何让C#识别非可空成员变量已完成初始化?
问题描述
我已经完成了所有非可空成员变量的初始化,但C#编译器无法识别,抛出CS8618警告。除了使用_elevatorPitch = default!;或#pragma warning disable CS8618之外,有没有其他方法消除这些警告?我希望保留警告功能,避免漏掉真正未正确初始化的情况,而且本次所有变量确实都完成了初始化。
相关代码
public CreateAppointmentDto(Event src, AppUser user, IBlobService blobService, IDocumentService documentService, IHttpClientFactory httpClientFactory, IEnumerable<Interest> allInterests) { AllInterests = allInterests; ApplyFromEvent(src, user, blobService); } public void ApplyFromEvent(Event src, AppUser user, IBlobService blobService) { ArgumentNullException.ThrowIfNull(src.Owner); ArgumentNullException.ThrowIfNull(src.Parent); ArgumentNullException.ThrowIfNull(src.Signups); if (src.AppointmentType == ExAppointmentType.OccurrenceInstance) throw new ArgumentException("Cannot edit an appointment that is an occurrence instance."); Event = src; Type = src.Type; _subject = src.Subject; _elevatorPitch = src.ElevatorPitch ?? string.Empty; HasAddress = src.HasAddress; AddressModel = new AddressFormPageModel(src.Address); Workplace = src.Workplace; Priority = src.Priority; MaxSignups = src.MaxSignups; Enabled = src.Enabled; Private = src.Private; Owner = src.Owner; ParentId = src.Parent.Id; Interest = src.Interest; Tags = src.Tags ?? new List<Tag>(); // bugbug DescriptionRichText!.RichText = src.Description; Thumbnail = new FileUploadModel(blobService, src.Thumbnail, SystemBlobs.EventUploadThumbnail); Picture = new FileUploadModel(blobService, src.Picture, SystemBlobs.EventUploadPicture); StartDateTime = src.StartDateTime.DateTime; EndDateTime = src.EndDateTime.DateTime; TimeZoneId = src.StartDateTime.TimeZoneId; var signup = src.Signups.FirstOrDefault(s => s.User.Id == user.Id); StatusId = (int)(signup?.Commitment ?? Signup.Mode.Unknown); AppointmentType = (AppointmentType)(int)src.AppointmentType; AllDay = src.ApptAllDay; RecurrenceInfo = src.RecurrenceInfo; Shifts = src.ApptShifts != null ? ShiftItemModel.FromShiftList(src.ApptShifts) : new List<ShiftItemModel>(); }
警告信息
2>C:\Git\LouisHowe\LouisHowe.web\PageModels\EventJob\CreateAppointmentDto.cs(276,10,276,30): warning CS8618: Non-nullable field '_subject' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. 2>C:\Git\LouisHowe\LouisHowe.web\PageModels\EventJob\CreateAppointmentDto.cs(276,10,276,30): warning CS8618: Non-nullable field '_elevatorPitch' must contain a non-null value when exiting constructor. Consider declaring the field as nullable. 2>C:\Git\LouisHowe\LouisHowe.web\PageModels\EventJob\CreateAppointmentDto.cs(276,10,276,30): warning CS8618: Non-nullable property 'TimeZoneId' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. 2>C:\Git\LouisHowe\LouisHowe.web\PageModels\EventJob\CreateAppointmentDto.cs(276,10,276,30): warning CS8618: Non-nullable property 'AddressModel' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. 2>C:\Git\LouisHowe\LouisHowe.web\PageModels\EventJob\CreateAppointmentDto.cs(276,10,276,30): warning CS8618: Non-nullable property 'Tags' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. 2>C:\Git\LouisHowe\LouisHowe.web\PageModels\EventJob\CreateAppointmentDto.cs(276,10,276,30): warning CS8618: Non-nullable property 'Thumbnail' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. 2>C:\Git\LouisHowe\LouisHowe.web\PageModels\EventJob\CreateAppointmentDto.cs(276,10,276,30): warning CS8618: Non-nullable property 'Picture' must contain a non-null value when exiting constructor. Consider declaring the property as nullable. 2>C:\Git\LouisHowe\LouisHowe.web\PageModels\EventJob\CreateAppointmentDto.cs(276,10,276,30): warning CS8618: Non-nullable property 'Shifts' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
解决方案
1. 使用MemberNotNull属性标注初始化方法(推荐)
C# 8.0及以上版本支持[MemberNotNull]属性,它能直接告诉编译器:调用指定方法后,列出的成员一定会被初始化。这种方法无需改动现有代码结构,同时保留警告的检测能力。
步骤:
- 引用
System.Diagnostics.CodeAnalysis命名空间 - 在
ApplyFromEvent方法上添加[MemberNotNull]属性,列出所有被初始化的非空成员
示例代码:
using System.Diagnostics.CodeAnalysis; public CreateAppointmentDto(Event src, AppUser user, IBlobService blobService, IDocumentService documentService, IHttpClientFactory httpClientFactory, IEnumerable<Interest> allInterests) { AllInterests = allInterests; ApplyFromEvent(src, user, blobService); } [MemberNotNull(nameof(_subject), nameof(_elevatorPitch), nameof(TimeZoneId), nameof(AddressModel), nameof(Tags), nameof(Thumbnail), nameof(Picture), nameof(Shifts))] public void ApplyFromEvent(Event src, AppUser user, IBlobService blobService) { // 原初始化逻辑保持不变 }
2. 将初始化逻辑内联到构造函数
C#的静态分析无法追踪跨方法的初始化操作,把ApplyFromEvent里的代码直接移到构造函数内部,编译器就能识别到所有成员都已完成初始化。
示例代码:
public CreateAppointmentDto(Event src, AppUser user, IBlobService blobService, IDocumentService documentService, IHttpClientFactory httpClientFactory, IEnumerable<Interest> allInterests) { AllInterests = allInterests; ArgumentNullException.ThrowIfNull(src.Owner); ArgumentNullException.ThrowIfNull(src.Parent); ArgumentNullException.ThrowIfNull(src.Signups); if (src.AppointmentType == ExAppointmentType.OccurrenceInstance) throw new ArgumentException("Cannot edit an appointment that is an occurrence instance."); Event = src; Type = src.Type; _subject = src.Subject; _elevatorPitch = src.ElevatorPitch ?? string.Empty; HasAddress = src.HasAddress; AddressModel = new AddressFormPageModel(src.Address); Workplace = src.Workplace; Priority = src.Priority; MaxSignups = src.MaxSignups; Enabled = src.Enabled; Private = src.Private; Owner = src.Owner; ParentId = src.Parent.Id; Interest = src.Interest; Tags = src.Tags ?? new List<Tag>(); Thumbnail = new FileUploadModel(blobService, src.Thumbnail, SystemBlobs.EventUploadThumbnail); Picture = new FileUploadModel(blobService, src.Picture, SystemBlobs.EventUploadPicture); StartDateTime = src.StartDateTime.DateTime; EndDateTime = src.EndDateTime.DateTime; TimeZoneId = src.StartDateTime.TimeZoneId; var signup = src.Signups.FirstOrDefault(s => s.User.Id == user.Id); StatusId = (int)(signup?.Commitment ?? Signup.Mode.Unknown); AppointmentType = (AppointmentType)(int)src.AppointmentType; AllDay = src.ApptAllDay; RecurrenceInfo = src.RecurrenceInfo; Shifts = src.ApptShifts != null ? ShiftItemModel.FromShiftList(src.ApptShifts) : new List<ShiftItemModel>(); }
3. 确保依赖的源对象成员非空
检查src.Subject、src.StartDateTime.TimeZoneId等被赋值的成员是否本身标记为非可空。如果这些成员是可空类型,即使在方法内赋值,编译器仍会认为存在null风险。可以在ApplyFromEvent开头添加非空检查:
public void ApplyFromEvent(Event src, AppUser user, IBlobService blobService) { ArgumentNullException.ThrowIfNull(src.Owner); ArgumentNullException.ThrowIfNull(src.Parent); ArgumentNullException.ThrowIfNull(src.Signups); ArgumentNullException.ThrowIfNull(src.Subject); // 添加该检查 ArgumentNullException.ThrowIfNull(src.StartDateTime.TimeZoneId); // 添加该检查 if (src.AppointmentType == ExAppointmentType.OccurrenceInstance) throw new ArgumentException("Cannot edit an appointment that is an occurrence instance."); // 剩余初始化逻辑不变 }
内容的提问来源于stack exchange,提问作者David Thielen
相关产品推荐
相关产品推荐

