在Java中,final关键字可以修饰变量使其变为最终变量,赋值后无法再修改。C#并没有直接对应final变量的语法,但提供了多种机制来实现类似的不可变效果,开发者可以根据变量的作用域、赋值时机等需求选择合适的方案。
C#实现不可变变量的常用方案
1. 使用const关键字
const关键字用于定义编译时常量,一旦定义就完全不可修改,且必须在声明时直接赋值,值必须是编译期可确定的常量表达式。
适用场景:定义全局固定不变的值,比如数学常量、固定配置项等。
// 定义const常量,声明时必须赋值
public const double Pi = 3.1415926;
public const string AppName = "MyApplication";
class Program
{
static void Main()
{
// 尝试修改const变量会直接编译报错
// Pi = 3.14; 这行代码无法通过编译
Console.WriteLine($"圆周率:{Pi}");
Console.WriteLine($"应用名称:{AppName}");
}
}
2. 使用readonly关键字
readonly关键字用于定义运行时常量,只能在声明时或者所属类的构造函数中赋值,一旦赋值后就无法再修改,比const更灵活,支持运行时确定值。
适用场景:实例级别的不可变字段,或者需要在运行时根据参数确定值的不可变变量。
public class User
{
// readonly字段,声明时赋值
public readonly string Id;
// readonly字段,构造函数中赋值
public readonly string Name;
public User(string id, string name)
{
Id = id;
Name = name;
// 构造函数外无法再修改readonly字段
}
public void UpdateName()
{
// 这里尝试修改会编译报错
// Name = "NewName";
}
}
class Program
{
static void Main()
{
User user = new User("1001", "张三");
Console.WriteLine($"用户ID:{user.Id},用户名:{user.Name}");
}
}
3. 使用不可变类型
如果需要整个对象的所有属性都不可修改,可以定义不可变类型,比如使用record类型(C# 9.0及以上支持),或者手动定义只有get访问器的属性。
// 使用record定义不可变类型,属性默认是init访问器,只能在初始化时赋值
public record Point(int X, int Y);
// 手动定义不可变类
public class ImmutableConfig
{
public string ServerUrl { get; }
public int Port { get; }
public ImmutableConfig(string serverUrl, int port)
{
ServerUrl = serverUrl;
Port = port;
}
}
class Program
{
static void Main()
{
Point p = new Point(10, 20);
// 尝试修改属性会编译报错
// p.X = 30;
ImmutableConfig config = new ImmutableConfig("http://ipipp.com", 8080);
// config.Port = 9090; 这行会编译报错
Console.WriteLine($"服务地址:{config.ServerUrl},端口:{config.Port}");
}
}
4. 局部不可变变量
在方法内部,如果需要定义不可修改的局部变量,可以使用var配合初始化,或者使用let?不对,C#中没有let,实际上局部变量如果不希望被修改,只需要遵循编码规范不重新赋值即可,也可以使用readonly修饰局部变量?不对,C#中readonly不能修饰局部变量,正确的方式是使用in参数或者编码约束,另外C# 7.0及以上支持ref readonly局部变量,不过更常用的还是通过编码规范保证局部变量不被修改,或者使用不可变集合。
如果是需要不可变的局部集合,可以使用System.Collections.Immutable命名空间下的不可变集合类型。
using System.Collections.Immutable;
class Program
{
static void Main()
{
// 创建不可变数组
ImmutableArray<int> numbers = ImmutableArray.Create(1, 2, 3, 4);
// 尝试修改会报错,不可变集合的方法会返回新的集合而不是修改原集合
// numbers[0] = 10; 编译报错
ImmutableArray<int> newNumbers = numbers.Add(5);
Console.WriteLine($"原集合长度:{numbers.Length},新集合长度:{newNumbers.Length}");
}
}
不同方案的区别对比
| 方案 | 赋值时机 | 作用范围 | 适用场景 |
|---|---|---|---|
| const | 声明时 | 全局/类级别 | 编译期确定的固定常量 |
| readonly | 声明时或构造函数 | 类/实例字段 | 运行时确定的实例不可变字段 |
| 不可变类型 | 初始化时 | 对象级别 | 整个对象属性不可修改 |
| 不可变集合 | 创建时 | 集合级别 | 集合元素不可修改 |
注意事项
- const变量是隐式静态的,不需要也不能加static修饰符,访问时直接通过类名访问。
- readonly字段如果是静态的,需要加static修饰符,且只能在静态构造函数中赋值。
- 不可变类型只是属性不能修改,如果属性是引用类型,引用类型内部的属性还是可以修改的,需要实现深不可变才能保证完全不可变。
总结:C#虽然没有名为最终变量的语法,但通过const、readonly、不可变类型、不可变集合等方案,完全可以覆盖Java最终变量的所有使用场景,开发者可以根据实际需求选择最合适的实现方式。
C#final_variablereadonlyconstimmutable修改时间:2026-06-11 13:36:40