如何声明C#属性:init为可空类型,get返回值始终非空?
问题描述
参考以下Owner类:
public sealed class Owner { public required Guid Id { get; init; } public string? DisplayName { get; init; } public string? GivenName { get; init; } public string? Surname { get; init; } public required string UserPrincipalName { get; init; } public string? Email { get { return field ?? UserPrincipalName; } init; } public bool AccountEnabled { get; init; } public string GetEmail() => Email ?? UserPrincipalName; }
该类实例由Microsoft Graph API返回的值初始化。当API返回的Email为null时,需使用UserPrincipalName作为收件邮箱。现在希望移除GetEmail()方法,直接通过Email属性获取非空的邮箱地址。由于表达式field ?? UserPrincipalName始终非空,希望让编译器知晓这一点,避免在使用Email时反复处理可空类型string?。
请问是否可以让编译器识别Email属性的init访问器接受string?类型,而get访问器返回string类型?
解决方案
可以通过手动声明后备字段的方式实现需求,让init访问器接受可空字符串,同时保证get访问器返回非空字符串:
public sealed class Owner { public required Guid Id { get; init; } public string? DisplayName { get; init; } public string? GivenName { get; init; } public string? Surname { get; init; } public required string UserPrincipalName { get; init; } // 手动声明可空的后备字段,允许init时传入null private string? _email; public string Email { // getter返回非空字符串:UserPrincipalName是required属性,初始化后必然非空 get => _email ?? UserPrincipalName; // init访问器接受可空字符串,对应Graph API返回的Email为空场景 init => _email = value; } public bool AccountEnabled { get; init; } }
原理说明
- 后备字段
_email设为string?,兼容API返回Email为空的情况,允许初始化时传入null。 Email属性的get访问器返回类型为string:因为UserPrincipalName是required修饰的非空属性,初始化后必然有值,所以_email ?? UserPrincipalName的结果始终非空,编译器会认可这一非空保证。- 修改后直接访问
Owner.Email即可得到非空的string类型,无需再做可空判断,同时可以移除冗余的GetEmail()方法。
如果需要更明确地向编译器标注get访问器的非空性,也可以结合[NotNull]属性(需引用System.Diagnostics.CodeAnalysis命名空间):
using System.Diagnostics.CodeAnalysis; public sealed class Owner { // ... 其他属性省略 ... private string? _email; [NotNull] public string? Email { get => _email ?? UserPrincipalName; init => _email = value; } }
不过第一种手动声明后备字段的方式更直观,且无需额外引入命名空间,是更推荐的实现方案。
内容的提问来源于stack exchange,提问作者Shuzheng
相关产品推荐
相关产品推荐

