init-only属性允许在对象初始化时设置值,之后不可修改,1. 它通过init访问器实现仅在构造函数或对象初始化器中赋值;2. 与readonly字段不同,它是属性,可被接口成员引用和反射识别;3. 与get; set;属性相比,它在初始化后禁止写入,确保不可变性;4. 适用于dto、值对象、线程安全场景和函数式编程;5. 在继承中,派生类可设置基类的init属性;6. 创建“修改版”对象需新建实例,推荐结合record类型使用with表达式简化操作;7. 多数序列化库支持init属性的反序列化。init-only属性为c#中的不可变数据建模提供了安全、简洁且编译器强制的解决方案。

C#中的
init-only
init-only
init
set
init
我们来看一个简单的例子。假设我们想定义一个表示产品信息的类,一旦产品ID和名称确定,就不应该再被更改:
public class Product
{
public int Id { get; init; } // 只能在初始化时设置
public string Name { get; init; } // 只能在初始化时设置
public string Description { get; set; } // 可以随时修改
// 构造函数可以用来初始化init属性
public Product(int id, string name)
{
Id = id;
Name = name;
}
}
// 如何使用它:
var myProduct = new Product(101, "智能手机")
{
Description = "最新款的智能手机,性能卓越。"
};
// 尝试修改init属性会引发编译错误:
// myProduct.Id = 102; // 编译错误:Init-only property or indexer 'Product.Id' can only be assigned in an object initializer, or on 'this' or 'base' in an instance constructor or an 'init' accessor.
// 而普通的set属性则可以修改:
myProduct.Description = "限时特惠,性能卓越。";
// 通过对象初始化器来设置init属性也完全没问题:
var anotherProduct = new Product(202, "无线耳机")
{
Description = "高音质,佩戴舒适。",
// Id = 202, // 也可以在这里设置,但如果构造函数已经设置了,就没必要了
// Name = "无线耳机"
};从上面的代码可以看出,
Id
Name
init-only
init-only
readonly
get; set;
理解
init-only
readonly
readonly
get
readonly
get; set;
init-only
init
get
简单来说:
readonly
get; set;
init-only
我觉得
init-only
readonly
get; set;
init-only
在我的项目实践中,
init-only
一个非常典型的场景是数据传输对象(DTOs)和值对象(Value Objects)。这些对象通常承载着一组数据,它们被设计成在创建后就不应该再发生变化。例如,一个表示订单详情的DTO,或者一个表示用户地址的值对象。一旦订单被创建,其订单号、商品列表、下单时间等核心信息就不应该被随意修改。使用
init-only
另一个让我觉得
init-only
此外,在函数式编程范式中,不可变性是一个核心原则。
init-only
init-only
最后,不得不提的是C# 9引入的record
record
init-only
record
init-only
record
init-only
init-only
虽然
init-only
首先是构造时的强制性。
init-only
init-only
int
null
Id { get; init; }init-only
在继承场景下,
init-only
init-only
set
public class BaseItem
{
public int BaseId { get; init; }
}
public class DerivedItem : BaseItem
{
public string DerivedName { get; init; }
public DerivedItem(int baseId, string derivedName)
{
BaseId = baseId; // 设置基类的init属性
DerivedName = derivedName;
}
}
var item = new DerivedItem(1, "SubItem");
// item.BaseId = 2; // 编译错误一个经常会遇到的问题是,当你有一个不可变对象,但你需要基于它创建一个“修改版”的新对象时,该怎么办?因为
init-only
// 假设Product是init-only属性的类
var originalProduct = new Product(101, "智能手机") { Description = "旧描述" };
// 如果要修改描述,需要创建一个新对象
var updatedProduct = new Product(originalProduct.Id, originalProduct.Name)
{
Description = "新描述" // 只修改Description
};
// 或者
var updatedProduct2 = new Product(originalProduct.Id, originalProduct.Name)
{
Description = "新描述",
// 如果Product有更多的init属性,需要一个个复制过来
// PropertyX = originalProduct.PropertyX,
// PropertyY = originalProduct.PropertyY
};而这正是
record
with
with
record
init
// 如果Product是一个record类型
public record ProductRecord(int Id, string Name)
{
public string Description { get; init; }
}
var originalRecord = new ProductRecord(101, "智能手机") { Description = "旧描述" };
// 使用with表达式创建新对象,并修改Description
var updatedRecord = originalRecord with { Description = "新描述" };
// originalRecord 保持不变
// updatedRecord 是一个新对象,Id和Name与originalRecord相同,但Description不同所以,如果你发现自己经常需要创建不可变对象的“修改版”,那么考虑将你的类改为
record
with
init-only
在序列化和反序列化方面,
init-only
init-only
init
总的来说,
init-only
以上就是C#的init-only属性如何实现不可变对象?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号