1.const 与 readonly 字段
-
核心区别:
- const:编译时常量,隐式静态,只能通过类名访问。必须在声明时赋值,仅支持基本类型(如数值、字符串)。
- readonly:运行时常量,属于实例级别,可在声明或构造函数中赋值,支持任意类型。
-
下例中的
name就是一个常量(const),age和length是只读字段(readonly),name需要使用类名来被访问,因为常量属于类本身而不是对象实例。
Person p = new Person(25, 100);
Console.WriteLine($"age={p.age}, name={Person.name}, length={p.length}");
class Person
{
public const string name = "Lisa"; // 类级别常量
public readonly int age; // 实例只读字段
public readonly double length = 18.8; // 声明时赋值
public Person(int age, double length)
{
this.age = age;
this.length = length; // 构造函数中赋值
}
}
-
优点:
- 直接存储数据:
readonly字段无属性访问逻辑,访问性能略高(差异通常微小)。
缺点:
- 暴露实现细节:
public readonly直接暴露字段,无法添加访问控制逻辑。 - 无法使用对象初始化器:只能在构造函数或声明时赋值。
- 直接存储数据:
2.只读属性(仅含 get 访问器)
通过设置只含get访问器来实现只读。
public class Rectangle
{
private double _width;
private double _height;
public double Area => _width * _height; // 计算属性
public Rectangle(double width, double height)
{
_width = width;
_height = height;
}
}
-
优点:
- 良好封装性:隐藏内部实现,可添加验证逻辑。
- 灵活性:支持计算属性或延迟加载。
缺点:
- 语法稍复杂:需额外定义字段和属性。
- 潜在性能开销:计算属性每次访问需重新计算,但简单返回字段的属性通常无显著开销。
3.Private set 访问器
-
核心特性
- 外部只读,内部可写:属性对外部代码是只读的,但类内部(包括构造函数和其他方法)可以修改其值。
- 灵活性:允许在类的生命周期内多次修改属性值,但对外保持只读。
Person person = new Person("Alice");
Console.WriteLine(person.Name); // 输出 "Alice"
person.Rename("Bob");
Console.WriteLine(person.Name); // 输出 "Bob"
// person.Name = "Charlie"; // 编译错误:外部无法直接修改
public class Person
{
public string Name { get; private set; }
public Person(string name)
{
Name = name; // 构造函数内赋值
}
public void Rename(string newName)
{
Name = newName; // 类内部方法修改值
}
}
-
优点
- 可控的封装性:对外隐藏写操作,同时保留类内部修改属性的灵活性。
- 支持延迟初始化:可在类的方法中按需初始化或更新属性值。
- 兼容旧版本:无版本限制(C# 1.0+ 均支持)。
-
缺点
- 非完全不可变:属性值可能在对象生命周期内被类内部代码多次修改,不适合严格不可变场景。
- 线程安全性问题:若多线程环境下类内部可能修改属性值,需自行加锁。
4.init 关键字(C# 9.0+)
-
核心特性:
- 允许在对象初始化期间(构造函数或初始化器)赋值,之后不可修改。
- 结合
get访问器实现不可变性。
Person p = new Person() { Name = "Bob" }; // 对象初始化器赋值
Console.WriteLine(p.Name); // 输出 "Bob"
class Person
{
public string Name { get; init; } = "Lisa"; // 声明时赋值
public Person()
{
Name = "Andy"; // 构造函数中赋值(会被初始化器覆盖)
}
}
-
优点:
- 不可变性:初始化后值不可变,适合构建安全数据模型。
- 对象初始化器支持:简化代码,提升可读性。
5.总结
| 特性 | readonly 字段 | 仅含 get 的属性 | init 关键字 | private set 属性 |
|---|---|---|---|---|
| 赋值时机 | 声明时或构造函数中 | 声明时或构造函数中 | 声明时、构造函数或初始化器 | 类内部任何时机 |
| 外部可读性 | 是(若 public) | 是 | 是 | 是 |
| 外部可写性 | 否 | 否 | 仅初始化器 | 否 |
| 内部可写性 | 否(构造函数外) | 否(需通过私有字段) | 否(初始化后不可变) | 是 |
| 适用场景 | 完全不可变的字段 | 计算属性或严格不可变 | 初始化后不可变的属性 | 外部只读、内部可变的属性 |