C#中init属性与仅get属性的区别及适用场景
C#中init属性与仅get属性的区别及适用场景
先看你给出的示例代码:
public class Person { public string Name { get; init; } public string Family { get; } }
这两个属性看似都是只读,但实际使用和赋值规则有明显差异,下面具体拆解:
核心差异:赋值时机与场景
1. 仅get属性({ get; })
这种属性的赋值权限非常严格:
- 只能在类的构造函数或者字段初始化器中赋值
- 对象实例创建完成后,完全无法修改该属性的值
示例代码:
public class Person { // 字段初始化器赋值 public string Family { get; } = "DefaultFamily"; // 构造函数赋值 public Person(string family) { Family = family; } } // 正确用法:通过构造函数初始化 var person1 = new Person("Smith"); // 错误:实例创建后无法修改,编译报错 // person1.Family = "Johnson";
2. init属性({ get; init; })
这种属性是C# 9.0引入的,兼顾了不可变性和初始化灵活性:
- 除了构造函数、字段初始化器,还支持在对象初始化器中赋值
- 一旦对象构造完成(离开初始化上下文),就再也无法修改该属性
示例代码:
public class Person { public string Name { get; init; } } // 正确用法:通过对象初始化器赋值 var person2 = new Person { Name = "Alice" }; // 错误:实例创建后无法修改,编译报错 // person2.Name = "Bob";
适用场景选择
用仅get属性的情况
- 当属性是对象的核心标识或不可变依赖,必须在创建时就确定,绝对不允许后续修改(比如用户ID、订单编号)
- 类的设计要求严格的不可变性,所有属性值都必须通过构造函数传入,避免任何意外修改的可能
用init属性的情况
- 你需要保持对象的不可变性,但又不想写大量构造函数重载,希望用更简洁的对象初始化器语法创建实例(尤其是属性较多时)
- 配合record类型使用时,init属性能很好地支持
with表达式进行对象克隆修改(比如var newPerson = oldPerson with { Name = "Bob" })
混合使用示例
实际开发中经常会混合两种属性,兼顾核心不可变性和初始化灵活性:
public class Person { // 核心ID必须通过构造函数赋值,绝对不可改 public string Id { get; } // 基础信息可以通过初始化器赋值,实例创建后不可改 public string Name { get; init; } public int Age { get; init; } public Person(string id) { Id = id; } } // 使用方式:构造函数传核心ID,初始化器补全其他属性 var person = new Person("user_123") { Name = "Charlie", Age = 30 };
内容的提问来源于stack exchange,提问作者Roohallah Azizi
相关产品推荐
相关产品推荐

