init访问器用于在对象初始化时赋值且之后不可变,支持构造函数和对象初始化器两种方式,相比set更安全,适用于配置、DTO等需不可变性的场景,提升代码健壮性与可读性。

C#中的
init
set
readonly
理解
init
init
set
init
一旦对象初始化过程完成,任何尝试修改
init
举个例子,假设我们有一个表示订单项的类:
public class OrderItem
{
public int ProductId { get; init; } // 只能在初始化时设置
public string ProductName { get; init; } // 只能在初始化时设置
public decimal UnitPrice { get; init; } // 只能在初始化时设置
public int Quantity { get; set; } // 可以随时修改
// 构造函数,也可以用来初始化init属性
public OrderItem(int productId, string productName, decimal unitPrice)
{
ProductId = productId;
ProductName = productName;
UnitPrice = unitPrice;
}
}
// 如何使用和初始化:
// 方式一:使用构造函数
var item1 = new OrderItem(101, "C#编程指南", 99.99m);
item1.Quantity = 2; // Quantity是set属性,可以修改
// 方式二:使用对象初始化器(更常见和灵活)
var item2 = new OrderItem(102, "ASP.NET Core实战")
{
UnitPrice = 129.50m,
Quantity = 1 // 这里给Quantity赋值,因为它有set访问器
};
// 错误示例:尝试修改init属性
// item1.ProductId = 200; // 这行代码会导致编译错误!
// item2.ProductName = "新的名称"; // 这行代码也会导致编译错误!通过这个例子,我们能清楚地看到
ProductId
ProductName
UnitPrice
Quantity
init
set
在我看来,
init
set
set
然而,
init
init
从技术实现上讲,当编译器看到
init
set
public class Configuration
{
// 使用init,表示这些配置项在程序启动后就不应再改变
public string ConnectionString { get; init; }
public int MaxRetries { get; init; } = 3; // 可以有默认值
// 使用set,表示这个属性是可变的,比如一个计数器
public int CurrentAttempts { get; set; }
public Configuration(string connectionString)
{
ConnectionString = connectionString;
}
}
var appConfig = new Configuration("Server=.;Database=AppDB")
{
MaxRetries = 5 // 通过对象初始化器设置init属性
};
// appConfig.ConnectionString = "新的连接字符串"; // 编译错误!
// appConfig.MaxRetries = 10; // 编译错误!
appConfig.CurrentAttempts = 1; // 允许,因为是set属性这种区别不仅仅是语法上的,它更深层次地影响了我们如何思考和设计类的行为。
init
init
这确实是个好问题,因为在C# 9之前,我们想实现属性的“只读”或“初始化后不可变”效果,主要依赖于
readonly
set
init
我个人认为,当你需要一个属性在对象创建时被赋值,并且之后不再改变,同时你又希望能够利用C#强大的对象初始化器语法时,
init
考虑以下几种情况:
大量的可选属性或配置项: 如果你的类有很多属性,其中大部分都是可选的,或者它们的值可以通过多种方式(比如从配置文件、环境变量或默认值)来提供。如果只用构造函数,你可能需要创建多个重载构造函数,或者一个参数列表非常长的构造函数,这会变得非常笨拙和难以维护。
// 传统方式,如果有很多属性,构造函数会很长或需要多个重载
// public Product(string name, string sku, decimal price, string description, int stock) { ... }
// 使用init属性,结合对象初始化器,代码更清晰
public class Product
{
public string Name { get; init; }
public string Sku { get; init; }
public decimal Price { get; init; }
public string Description { get; init; }
public int Stock { get; init; }
public Product(string name, string sku, decimal price) // 核心属性可以通过构造函数确保
{
Name = name;
Sku = sku;
Price = price;
}
}
// 初始化时非常灵活和可读
var newProduct = new Product("笔记本电脑", "LAPTOP-001", 1299.99m)
{
Description = "高性能轻薄笔记本",
Stock = 50
};
var minimalProduct = new Product("鼠标", "MOUSE-002", 25.00m);这里,
Description
Stock
构建不可变数据传输对象(DTOs)或值对象: 这些对象通常只用于封装数据,并且一旦创建就不应被修改。
init
与记录类型(Records)结合使用: C# 9引入的记录类型(
record
init
init
init
相比之下,
readonly
set
init
set
init
所以,我的建议是,当你的属性需要在对象创建后保持不变,并且你欣赏对象初始化器带来的简洁和可读性时,毫不犹豫地选择
init
init
从实际项目开发的视角来看,
init
提升代码健壮性与可预测性: 这是最直接的好处。当一个对象的某些属性被声明为
init
init
简化并发编程: 在多线程或异步编程中,可变状态是导致死锁、竞态条件等问题的罪魁祸首。
init
更好的API设计和可读性: 对象初始化器与
init
// 假设一个复杂的过滤器对象
public class ProductFilter
{
public string Keyword { get; init; }
public decimal? MinPrice { get; init; }
public decimal? MaxPrice { get; init; }
public string Category { get; init; }
public bool IncludeOutOfStock { get; init; } = false; // 默认值
// 如果没有init,可能需要一个超长构造函数或者setter
}
// 使用init和对象初始化器,创建过滤器非常清晰
var filter = new ProductFilter
{
Keyword = "手机",
MinPrice = 1000m,
MaxPrice = 5000m,
Category = "电子产品"
};这样的代码,一眼就能看出每个属性的含义,而不需要去查阅构造函数的签名。
支持记录类型(Records)的强大功能: 记录类型是C# 9引入的一个重要特性,它默认的属性就是
init
Equals
GetHashCode
ToString
with
init
init
提高测试效率: 不可变对象更容易进行单元测试。因为它们的状态在创建后不会改变,所以你可以更确定地知道测试的输入条件和预期结果,而无需担心对象在测试过程中被意外修改,从而简化了测试用例的编写和维护。
总的来说,
init
以上就是C#的init访问器有什么用途?如何初始化属性?的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号