主构造函数是将参数声明嵌入类型定义以消除样板代码的语法,写法为public class Person(string name, int age),支持class/struct/record但行为不同,不支持static或abstract类,参数仅作用于初始化上下文且不可直接访问。

主构造函数不是“多了一个构造函数”,而是把参数声明直接嵌入类型定义本身——它删掉了你写 public Person(string name, int age) { Name = name; Age = age; } 这类样板代码的必要性。
主构造函数怎么写,哪些类型能用
语法就是类名后紧跟括号:public class Person(string name, int age)。这个写法本身就是一个完整、合法的类定义,不需要再补构造函数体。
- 支持
class、struct、record—— 三者都可用,但行为略有差异:record 的参数默认生成公开属性,而普通 class/struct 的参数只是“作用域内的局部变量”,必须显式绑定到字段或属性才能对外暴露 - 不支持
static class或abstract class声明主构造函数(编译报错) - 结构体即使写了主构造函数,
default(Point)仍会返回全零值,不会触发主构造逻辑
参数怎么用,为什么不能直接访问
主构造参数(如 name、age)在类体内任何地方都可见,但它们不是字段、不是属性、也不是成员——只是参数。所以你不能在外部写 person.name,也不能在内部用 this.name(会报错“无法通过 this 访问参数”)。
- 正确用法是直接在属性初始化中引用:
public string Name { get; } = name; - 也可在方法里用:
public override string ToString() => $"{name} ({age})"; - 若想存为私有字段,得手动写:
private readonly string _name = name;(注意:C# 没有field关键字,这只是常见命名习惯) - 别试图在字段初始值设定项之外的地方给参数重新赋值——它不可变,也不允许赋值
继承时怎么调用基类构造函数
如果派生类也用了主构造函数,且基类有主构造函数,就必须在派生类声明末尾用 : base(...) 显式调用,否则编译失败。
- 例如:
public class Student(string name, int age) : Person(name, age + 1) { }—— 这里name和age可直接用于基类调用表达式 - 如果基类没有主构造函数,但有带参构造函数,也必须显式调用:
public class Worker(ILogger<worker> logger) : BackgroundService() { _logger = logger; }</worker> - 一旦用了主构造函数,编译器就不再自动生成无参构造函数;如果其他代码依赖无参构造(比如某些序列化库),就得额外加一个
public Person() : this("unknown", 0) { }
和传统构造函数共存会怎样
不能同时存在主构造函数和显式的实例构造函数——这是硬性限制。下面这段代码会编译失败:
public class BadExample(string name) {
public BadExample() { } // ❌ 编译错误:不能同时定义主构造函数和实例构造函数
}如果你需要多个构造入口,只能放弃主构造,改回传统写法;或者保留主构造,用 this(...) 链式调用方式定义辅助构造函数:
public class GoodExample(string name, int age) {
public GoodExample(string name) : this(name, 0) { }
public GoodExample() : this("anonymous", 0) { }
}真正容易被忽略的是:主构造参数在字段初始值设定项中就已可用,但此时基类字段还没初始化完毕——所以别在字段初始化里调用虚方法或依赖基类状态,顺序陷阱比传统构造函数更隐蔽。


















