##

在 C# 中,{ get; set; }{ get; init; } 都是用于定义类的属性访问器,但它们在行为上有显著的差异。{ get; set; } 是传统的属性访问器,而 { get; init; } 是 C# 9.0 引入的新特性,主要用于创建不可变对象的属性。本文将深入探讨这两种访问器的区别,帮助开发者更好地理解它们的使用场景。

1. 什么是 { get; set; }

{ get; set; } 是 C# 中最常见的属性访问器,它定义了一个属性的 gettersetter,允许你在对象生命周期的任何时刻获取和修改属性的值。使用 { get; set; } 定义的属性可以随时被修改,适用于那些需要动态修改的场景。

示例:

public class Person
{
    public string Name { get; set; }  // 可读可写
}

var person = new Person();
person.Name = "Alice";  // 可以修改属性
Console.WriteLine(person.Name);  // 输出:Alice

person.Name = "Bob";  // 可以再次修改
Console.WriteLine(person.Name);  // 输出:Bob

特点:

  • 可读可写:属性值可以随时修改。
  • 灵活性高:适用于需要动态修改属性的场景。
  • 对象生命周期内可修改:可以在对象的任何时刻更新属性的值。

2. 什么是 { get; init; }

{ get; init; } 是 C# 9.0 中新增的特性,主要用于定义 只读 属性,限制了属性的值只能在对象初始化时进行设置。使用 init 访问器,你可以在对象初始化器中设置属性的值,但一旦对象被创建,属性值就无法再修改。这种机制有助于创建不可变对象,增强了代码的不可变性(immutability)。

示例:

public class Person
{
    public string Name { get; init; }  // 只可在对象初始化时设置
}

var person = new Person { Name = "Alice" };  // 只能在初始化时设置值
Console.WriteLine(person.Name);  // 输出:Alice

// person.Name = "Bob";  // 错误:无法修改属性值

特点:

  • 不可修改:属性值只能在对象初始化时设置,初始化后无法修改。
  • 不可变对象:适用于需要创建只读或不可变对象的场景。
  • 对象初始化时赋值:可以通过对象初始化器给属性赋值,但一旦初始化完毕,属性值不可再变。

3. { get; set; }{ get; init; } 的主要区别

特性 { get; set; } { get; init; }
修改限制 可以在对象生命周期中的任何时刻修改属性值 只能在对象初始化时修改属性值,初始化后不可修改
使用场景 常用于需要动态修改属性值的类 适用于需要确保数据不可变(immutable)或对象只读的场景
赋值方式 赋值可在对象初始化后任何时刻进行 赋值只能在对象初始化时进行
语法 { get; set; } { get; init; }

4. 示例对比

4.1 { get; set; } 示例

public class Person
{
    public string Name { get; set; }
}

var person = new Person();
person.Name = "Alice";  // 可以修改
Console.WriteLine(person.Name);  // 输出:Alice

person.Name = "Bob";    // 可以再次修改
Console.WriteLine(person.Name);  // 输出:Bob

在上述示例中,Name 属性可以在对象创建后随时修改,这对于动态数据或需要频繁更新的属性非常有用。

4.2 { get; init; } 示例

public class Person
{
    public string Name { get; init; }
}

var person = new Person { Name = "Alice" };  // 只能在初始化时设置
Console.WriteLine(person.Name);  // 输出:Alice

// person.Name = "Bob";  // 错误:无法修改属性值

在这个示例中,Name 属性的值只能在对象初始化时设置。一旦初始化完成,属性值就无法再改变,这对于不可变对象(immutable object)或要求数据一致性的场景特别有用。

5. 为什么使用 { get; init; }

使用 { get; init; } 可以帮助你设计 不可变对象(immutable objects),从而确保对象的状态在创建之后不会被修改。不可变对象有很多优点:

  • 线程安全:因为不可变对象的状态不会改变,它们天然是线程安全的。
  • 数据一致性:不可变对象能够确保数据的一致性,避免了多线程环境下的数据竞争和不一致性问题。
  • 更易于调试和维护:由于属性值在对象初始化后无法被修改,代码更具可预测性和可维护性。

这种模式在很多领域都非常有用,尤其是在设计 值对象(Value Object)DTO(数据传输对象) 时,常常需要确保对象在传输或使用过程中不发生变化。

6. 总结

  • { get; set; }:提供了可读可写的属性访问器,适用于需要动态修改属性值的类。它允许在对象生命周期内的任何时刻修改属性。

  • { get; init; }:只允许在对象初始化时设置属性值,初始化后无法修改。这种访问器适用于不可变对象,确保属性在对象创建后不会被修改。

选择使用 { get; set; } 还是 { get; init; },取决于你的应用场景。如果你需要灵活地修改对象的属性,{ get; set; } 是最佳选择;如果你希望确保对象的属性在创建后保持不可变,则可以使用 { get; init; }