required修饰符仅在编译期强制对象初始化器中显式赋值所有标记成员,漏写即报cs8852;必须带可访问的set或init访问器,且可见性不低于类本身;仅支持初始化器语法,构造函数需加[setsrequiredmembers]才被认可;反序列化和反射完全绕过该检查。

required 修饰符不是运行时校验,它只在编译期强制对象初始化器中显式写出所有标记成员——漏一个就报 CS8852,不写直接过不了编译。
required 属性必须带可访问的 set 或 init 访问器
很多人写 public required string Name { get; init; } 是合法的,但写 public required string Name { get; } 或 public readonly string Name; 就会触发 CS8853 错误。原因很简单:required 依赖编译器生成的隐式赋值逻辑,而该逻辑需要一个“能写进去”的入口。
-
set和init都行,但可见性不能低于类本身(比如public class里不能有protected required string Name) -
private set、internal init都可以;protected init不行(可见性降级) -
init虽然语义上是“构造期间一次性写”,但它仍满足 required 的契约;而只读属性或字段完全不满足
对象初始化器是 required 唯一认的路径
只要用了 required,你就只能走 new TypeName { Prop = value } 这条路。其他方式哪怕逻辑上等价,编译器也一律不认:
-
var p = new Person(); p.Name = "Alice";→ 报 CS8852(两步分开,不算初始化器) -
var p = new Person("Alice");→ 即使构造函数里写了this.Name = name;,依然报错 -
var p = new Person { Name = "Alice" };→ 如果还有另一个required int Age没写,照样报 CS8852
根本原因:required 是对“初始化表达式”的语法树检查,不是控制流分析。它不看构造函数里写了啥,只看你 new 的时候有没有在大括号里列全。
想用构造函数绕过?得加 [SetsRequiredMembers]
普通构造函数不会自动满足 required 约束。必须手动打上 [SetsRequiredMembers] 特性,才能让编译器“信你这一回”:
-
[SetsRequiredMembers] public Person(string name, int age) { Name = name; Age = age; }→ 合法 - 但注意:这个特性只是断言,不校验。如果漏写
Age = age;,运行时Age就是 0,毫无提示 -
record Person(string Name, int Age);→ 主构造函数自动带该特性,且禁用无参构造,更安全
反序列化和反射完全绕过 required 检查
这是最容易被忽略的一点:required 是纯编译期机制,对运行时创建的对象毫无约束:
-
JsonSerializer.Deserialize<person>(json)</person>默认不校验 required 字段,结果可能是Name = null,Age = 0 -
Activator.CreateInstance<person>()</person>能成功,且不抛异常 - 若真要 JSON 层面也强制,得用
[JsonRequired]或启用JsonSerializerOptions.RespectRequiredConstructorParameters = true
也就是说,如果你的 Person 类既用于 API 接收 JSON,又用于本地 new,那 required 只保住了后者——前者还得靠额外配置或运行时校验补位。










