⚙️ C# 中的 init
、readonly
和 const
有什么区别?
在 C# 编程中,我们经常需要定义 不可变(immutable) 的数据。那么该用 const
、readonly
,还是 init
呢?这三者虽然都可用于定义不可变成员,但它们的应用场景和语法限制却大不相同。本文将通过对比和示例,帮助你理清它们的区别与用法。
📌 1. 简要对比表
特性 | const |
readonly |
init |
---|---|---|---|
赋值时机 | 定义时 | 构造函数或定义时 | 初始化时(对象构造期间) |
是否编译期常量 | ✅ 是 | ❌ 不是 | ❌ 不是 |
可用于字段 | ✅ | ✅ | ✅(属性) |
可用于属性 | ❌ | ✅(支持 get-only) | ✅(支持 set-only-once) |
赋值位置 | 定义时 | 构造函数、字段初始化 | 对象初始化表达式 {} 中 |
常用于 | 全局常量、数学值 | 构造后不变的实例字段 | 构造后不变但支持对象初始化的属性 |
🧱 2. const
------ 编译期常量
csharp
public class Constants
{
public const double Pi = 3.14159;
}
✅ 特点:
- 必须在定义时赋值
- 编译为常量值,直接嵌入调用代码中
- 只能修饰 基本类型 、
string
等编译期常量
⚠️ 注意:
- 修改
const
值需 重新编译所有引用它的程序集,否则可能仍然使用旧值!
🧱 3. readonly
------ 运行期常量(构造后不可变)
csharp
public class Config
{
public readonly string Version;
public Config(string version)
{
Version = version;
}
}
✅ 特点:
- 可以在定义时或构造函数中赋值
- 适合依赖运行期值(如时间戳、GUID、配置文件)
csharp
public readonly DateTime StartTime = DateTime.Now;
⚠️ 注意:
readonly
不能在构造函数外被修改- 可用于字段,常配合
private
修饰
🧱 4. init
------ 初始化器赋值(C# 9 引入)
csharp
public class Person
{
public string Name { get; init; }
public int Age { get; init; }
}
✅ 特点:
- 支持对象初始化表达式语法
{}
中赋值 - 一旦对象构造完成,属性就不可更改
csharp
var p = new Person { Name = "Alice", Age = 30 };
// p.Age = 31; // ❌ 编译错误
👍 优势:
- 与面向对象封装天然适配
- 保留不可变性同时支持初始化灵活性
🚩 5. 使用建议总结
使用场景 | 推荐关键词 | 理由 |
---|---|---|
编译期不会变化的值(如 Pi) | const |
编译时已确定,性能更优 |
运行期动态初始化的字段 | readonly |
构造函数中灵活赋值,构造后不可变 |
想使用初始化表达式设定属性值 | init |
可读性强,避免创建多个构造函数 |
想实现不可变对象(immutable) | init + get |
提供只读接口,同时允许构造期间初始化 |
✅ 总结
关键词 | 可变性 | 赋值时机 | 使用场景 |
---|---|---|---|
const |
永远不可变 | 定义时 | 编译期常量 |
readonly |
构造后不可变 | 构造函数或定义时 | 运行期常量,依赖构造动态赋值 |
init |
构造后不可变 | 对象初始化表达式期间 | 封装属性,构造时赋值灵活 |
选择合适的不可变策略,可以帮助你写出更安全、清晰、易维护的代码!
📬 你更常用哪种不可变成员定义方式?欢迎评论区分享你的使用经验!
如果你觉得这篇文章有帮助,别忘了点赞、收藏和关注我~ 🚀