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

C#中初始化不可空类属性是否仅依赖构造函数?含C#8场景疑问

关于C#中不可空属性初始化的解决方案

好问题!在启用了nullable引用类型的C#项目里,初始化不可空属性真的不止构造函数这一种路子,咱们根据你的场景和C#版本来逐一拆解:

先搞懂为什么编译器报错

你当前的Request类里,三个string属性都是非可空引用类型,但自动属性的默认初始化会把它们设为null——编译器没法保证你在使用这些属性前一定会赋值,所以才抛出警告/错误。

构造函数不是唯一方案,不同C#版本有不同解法

1. C# 11及以上:用required属性(最贴合你的需求)

这是最完美的解决方案!只要给不可空属性加上required修饰符,编译器就会强制要求你在对象初始化器中给所有required属性赋值,少一个都不行。

修改你的类:

public class Request {
    public required string Type { get; set; }
    public required string Username { get; set; }
    public required string Key { get; set; }
}

这时候如果写:

var request = new Request { Type = "Not null", Username = "Not null" };

编译器会直接报错,提示你Key属性未被初始化,完全符合你的预期。而且不用写冗长的构造函数,20个属性的话也只需要给每个加个required就行。

2. C# 9及以上:记录类型(Record)

如果你的项目能升级到C#9,记录类型会自动帮你生成包含所有属性的构造函数,同时支持位置参数和对象初始化器语法,写法极简:

public record Request(string Type, string Username, string Key);

使用时既可以用位置参数:

var request = new Request("Not null", "Not null", "Not null");

也可以用对象初始化器:

var request = new Request { Type = "Not null", Username = "Not null", Key = "Not null" };

而且记录类型默认是不可变的(属性为init-only),如果需要可变的话,也可以显式定义为set。

3. C# 8:退而求其次的方案

因为C#8没有required和记录类型,编译时强制不可空属性初始化的话,构造函数确实是最可靠的方式。但20个属性写构造函数太麻烦?可以试试这两个思路:

  • Builder模式:创建一个RequestBuilder类,每个属性的设置方法返回自身,最后提供一个Build()方法,在方法内检查所有必填属性是否都已赋值,未赋值则抛出异常。比如:
    public class RequestBuilder {
        private string? _type;
        private string? _username;
        private string? _key;
    
        public RequestBuilder WithType(string type) {
            _type = type;
            return this;
        }
    
        public RequestBuilder WithUsername(string username) {
            _username = username;
            return this;
        }
    
        public RequestBuilder WithKey(string key) {
            _key = key;
            return this;
        }
    
        public Request Build() {
            if (_type == null || _username == null || _key == null) {
                throw new InvalidOperationException("所有必填属性都必须赋值");
            }
            return new Request {
                Type = _type,
                Username = _username,
                Key = _key
            };
        }
    }
    
    使用时链式调用:
    var request = new RequestBuilder()
        .WithType("Not null")
        .WithUsername("Not null")
        .WithKey("Not null")
        .Build();
    
    这是运行时检查,但能避免写长构造函数,代码可读性也不错。
  • 代码分析规则:启用CA1062规则(验证公共方法的参数),配合nullable警告,能在编译时提醒你有没有遗漏赋值,但没法像required那样强制拦截。

为什么属性初始化器不适合你的场景

你提到的属性初始化器(比如public string Type { get; set; } = "default";)确实只适合那些有合理默认值的属性,像Type这种不能随便赋初始值的场景,强行加默认值反而会引入潜在的bug,所以这个方案确实不适用。

内容的提问来源于stack exchange,提问作者Ilya Chernomordik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 09:04:36