主构造函数是将参数直接写在类声明行末尾(如public class person(string name, int age)),编译器自动生成私有只读字段和对应构造逻辑;参数仅在类体内可见,需显式赋值给字段或属性才能持久化,且所有额外构造函数必须以this(...)首行调用它。

主构造函数不是“加一个新构造函数”,而是把参数直接写在 class 或 struct 声明行末尾,让编译器自动生成字段捕获和构造逻辑——它只省代码,不改语义,也不自动暴露属性。
怎么写一个带 primary constructor 的 class
语法就是类名后紧跟括号参数,比如 public class Person(string name, int age)。这行本身已声明主构造函数,编译后会生成一个 public 实例构造函数,并为每个参数生成同名的 private readonly 字段(如 private readonly string name;)。
- 参数不能是
ref、out、params,也不能带可空引用类型标注(如string?),但可以写string,其可空性由上下文推断 - 参数名大小写敏感,且作用域覆盖整个类体(可在属性 getter、方法、初始化表达式中直接使用)
- 不支持函数体:你不能在括号后加
{ ... }来写校验或日志,它纯属声明,无执行逻辑 - 若想让某个参数变成 public get-only 属性,用
public修饰该参数:public class Person(public string Name, int age)——此时Name不再生成私有字段,而是生成自动属性
如何访问主构造参数并暴露为公共成员
主构造参数默认不对外可见,也不自动成为属性。你必须显式定义字段、属性或方法来暴露它们。
- 最常见方式是用只读属性初始化:
public string Name { get; } = name;——注意这不是 auto-property,而是用参数值做初始化表达式 - 也可以手动赋值给
private readonly字段:private readonly string _name = name;,再通过public string Name => _name;暴露 - 不能直接在外部调用
person.name,因为name是构造参数,不是字段也不是属性,仅在类体内有效 - 方法中可直接用:
public override string ToString() => $"{name} ({age})";
主构造函数和普通构造函数能共存吗
可以,但所有额外构造函数都必须显式调用主构造函数,用 this(...),且只能调一次。
- 例如:
public Person() : this("Unknown", 0) { }是合法的;漏掉this(...)会报错CS9148 - 无参构造、重载构造、带校验的构造,全都要以
this(...)开头 - 不能同时存在主构造函数和传统实例构造函数签名完全相同的显式构造函数,比如写了
class C(int x),就不能再写public C(int x) { ... },会编译失败 - 校验逻辑通常放在私有构造函数里,再让主构造函数委托过去(反过来不行,因为主构造必须最先被调)
继承时怎么把参数传给基类
主构造函数不会自动调用基类构造函数,必须显式用 : base(...),且参数可直接引用主构造参数名。
- 例如:
public class Student(string name, int age) : Person(name, age + 1) { }——这里name和age就是主构造参数,可直接用于基类调用 - 如果基类没有匹配的构造函数,编译直接报错,不会 fallback 到无参构造
- 泛型类的主构造参数不能直接参与约束声明(如
where T : new()不受主构造影响),约束仍需单独写 - record 类型不支持主构造函数语法(它已有自己的紧凑构造机制),
partial类也不支持
最容易被忽略的是:主构造参数不是字段,也不是属性,只是作用域在类体内的“临时绑定值”;你一旦没把它赋给字段或属性,它就在构造结束时消失——哪怕你只在 ToString() 里用了一次,也得确保它被持久化,否则运行时报 NullReferenceException 都查不到源头。











