C#长久以来通过readonly字段支持不可变对象,但C# 9引入的init关键字带来了更灵活的不可变性控制方案。这个看似简单的语法糖,能在保持代码简洁性的同时,为你的对象构建过程加上编译时安全锁。
🔍 init是什么?
表面看init类似属性setter,但它的核心区别在于:仅允许在对象初始化阶段赋值,构造完成后立即变为只读状态。
传统可变属性:
public class Person
{
public string Name { get; set; } // 随时可修改
public int Age { get; set; }
}
使用init的不可变属性:
public class Person
{
public string Name { get; init; } // 仅初始化时可设值
public int Age { get; init; }
}
🚀 init的三大优势
var person = new Person { Name = "Alice" };
person.Name = "Bob"; // ❌ CS0272 不可修改
// 初始化阶段仍可使用流畅语法
var product = new ProductDTO {
Id = 1001,
Name = "Surface Pro"
};
public class ApiResponse<T>
{
public T Data { get; init; }
public DateTime Timestamp { get; init; } = DateTime.UtcNow;
}
⚡ 与readonly的终极对比 | 特性 | readonly字段 | init属性 | |---------------------|---------------------|-----------------------| | 赋值时机 | 仅构造函数内 | 对象初始化阶段 | | 是否支持初始化器语法 | ❌ 不支持 | ✅ 完美支持 | | 适用场景 | 完全不可变的核心字段 | 需初始化灵活性的模型 |
🔥 与record的黄金组合
C# 9的record类型本身具有值语义不可变性,配合init实现双重保护:
public record FinancialTransaction(
decimal Amount,
string Currency)
{
public Guid TransactionId { get; init; } = Guid.NewGuid();
}
// 使用示例
var tx = new FinancialTransaction(99.99m, "USD") {
TransactionId = Guid.Parse("...")
};
💡 设计决策指南
readonly
字段+构造函数init
属性record
+init
🎯 最佳实践场景
init关键字虽小,却是C#不可变性设计的重要拼图。它巧妙地填补了readonly严格限制与setter完全自由之间的空白,让开发者能以最小成本获得最大安全性。下次设计模型时,不妨让init成为你的首选武器。