You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何声明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; }
}

原理说明

  1. 后备字段_email设为string?,兼容API返回Email为空的情况,允许初始化时传入null。
  2. Email属性的get访问器返回类型为string:因为UserPrincipalName是required修饰的非空属性,初始化后必然有值,所以_email ?? UserPrincipalName的结果始终非空,编译器会认可这一非空保证。
  3. 修改后直接访问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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 20:38:12