const, readonly和static的区别

本文详细对比了C#中的const、static readonly及readonly的特性与使用区别,包括它们在编译期与运行期的行为差异,以及各自适用的场景。

他们本质的差别在于,const的值是在编译时确定的,因此只能在声明时通过常量表达式指定其值。而static readonly是在运行时计算出其值的,所以还能通过静态构造函数来赋值。

 

(一)Const:

1)常量是在编译时定义的,不能在运行时改变;

     不能在代码的任何地方改变常量,要不然编译出错。

2)只能在声明中赋值,且只能是常数或常数表达式;

     出错的例子:

     const double PI;
     PI=3.14159;

3)类或构造函数在运行时通过new来初始化,所以不能设常量为类或构造函数。

     出错的例子:

     const int [] birds = new int[] {1, 2, 3};

4)不允许在常数声明中使用 static 修饰符。隐式地为静态的。

     出错的例子:

     static const double PI=3.14159;

 

(二)readonly

1)readonly是在运行时定义的,不同的构造函数会有不同的值。

2)readonly可以在声明时初始化,或者在构造函数中初始化。

3)可以用new来初始化。

4)不是隐式static, 所以可以用static 修饰。

 

(三)static

1)使用 static 修饰符声明属于类型本身而不是属于特定对象的静态成员。

2)不能通过实例引用静态成员,只可以通过类型名称引用它。

3)每个静态字段只有一个副本,不管有多少个类的实例,它们共享一个副本。

4)不可以使用 this 引用静态方法或属性访问器。 因为没有this指针。

5)static类中只能包含static成员,不能包含任何实例数据或方法。 

 

 

学习网址:

http://en.csharp-online.net/const%2C_static_and_readonly
http://blog.youkuaiyun.com/Active_Life/archive/2009/03/02/3948587.aspx

<think>我们正在讨论C#中constreadonly关键字的区别。根据用户提供的引用,我们可以总结如下: 1. **初始化时机**: - `const`:必须在声明时初始化,且必须是编译时可确定的常量表达式[^3][^4]。 - `readonly`:可以在声明时初始化,也可以在构造函数中初始化(对于实例字段)或在静态构造函数中初始化(对于静态字段)[^3][^4]。 2. **赋值次数**: - `const`:只能赋值一次(即在声明时)[^3][^4]。 - `readonly`:在声明时或构造函数中赋值,之后不能修改(但可以通过反射修改)[^4]。 3. **数据类型支持**: - `const`:仅支持基元类型(如int, float, char, bool等)、stringnull引用[^3][^4]。 - `readonly`:支持所有类型(包括自定义类结构体)[^3][^4]。 4. **默认值**: - `const`:必须显式初始化,没有默认值[^4]。 - `readonly`:如果不显式初始化,值类型字段默认为0,引用类型默认为null[^4]。 5. **性能**: - `const`:零开销访问,因为值在编译时被直接嵌入到使用它的地方[^4]。 - `readonly`:有微小访问开销,因为它作为实例字段或静态字段存储在内存中[^4]。 6. **线程安全**: - `const`:天然线程安全,因为它是编译时常量[^4]。 - `readonly`:实例字段需要注意可见性(因为不同线程可能看到不同的构造函数初始化的值),静态字段是线程安全的(因为静态构造函数只执行一次)[^4]。 7. **反射修改**: - `const`:无法通过反射修改[^4]。 - `readonly`:可以通过反射强制修改(但一般不推荐)[^4]。 8. **使用场景**: - `const`:用于声明永远不会改变的常量,如数学常数、枚举值等[^3][^4]。 - `readonly`:用于在运行时初始化后不再改变的值,如依赖注入的对象、配置值等[^3][^4]。 下面我们通过代码示例来说明: ```csharp public class ConstAndReadonlyExample { // const 必须在声明时初始化 public const int MaxValue = 100; // 正确 // public const DateTime MinDate = DateTime.MinValue; // 错误:const不支持DateTime类型 // readonly 可以在声明时初始化 public readonly int MinValue = 0; // 正确,也可以不初始化(则默认为0) // 也可以在构造函数中初始化 public readonly int MaxValue2; public readonly DateTime CreatedAt; // 静态readonly public static readonly int StaticValue; // 静态构造函数中初始化静态readonly static ConstAndReadonlyExample() { StaticValue = 200; } public ConstAndReadonlyExample(int maxValue2) { // 在实例构造函数中初始化readonly字段 MaxValue2 = maxValue2; CreatedAt = DateTime.Now; // 运行时常量 } public void TryChange() { // 以下代码会编译错误,因为readonly字段不能在方法中修改 // MinValue = 10; } } ``` **使用建议**: - 当值在编译时已知且永远不会改变时,使用`const`(例如数学常数、固定的字符串等)。 - 当值在运行时才能确定(如从配置文件中读取)或需要支持非基元类型时,使用`readonly`。 **注意**:`const`常量是静态的,因此通过类名访问;而`readonly`字段可以是实例字段(通过对象访问)或静态字段(通过类名访问)。 最后,引用[^5]提醒:不要使用常量来表示你随时要更改的值。因为`const`的值在编译时被嵌入,如果修改了`const`的值,所有引用它的程序集都需要重新编译才能看到更新。 相关问题: 1. 为什么`const`只支持基元类型、字符串null? 2. 如何通过反射修改`readonly`字段? 3. 在构造函数中初始化`readonly`字段有什么注意事项? 4. `readonly`字段属性有什么区别? 5. 在C#中,如何定义常量数组或集合?</think>### C#中`const`与`readonly`关键字的区别详解 #### 1. **核心差异总结** | 特性 | `const` 常量 | `readonly` 只读字段 | |--------------------|---------------------------------------|--------------------------------------| | **初始化时机** | 声明时必须初始化[^3][^4] | 可在声明时或构造函数中初始化[^3][^4] | | **值确定时机** | 编译时确定(编译时常量)[^3][^4] | 运行时确定(运行时常量)[^3][^4] | | **数据类型支持** | 仅支持基元类型、`string``null`[^4] | 支持所有数据类型(含自定义类型)[^4] | | **赋值限制** | 仅能赋值一次(声明时)[^3] | 可在构造函数中重新赋值[^3] | | **内存分配** | 零开销(值直接嵌入IL)[^4] | 微小开销(作为字段分配内存)[^4] | | **线程安全** | 天然线程安全[^4] | 需注意实例字段可见性[^4] | | **反射修改** | 不可修改[^4] | 可通过反射强制修改[^4] | --- #### 2. **详细对比分析** 1. **初始化要求** - `const`:**必须**在声明时初始化,且必须是编译时可确定的常量表达式 ```csharp public const int MAX_SIZE = 100; // √ 合法 // public const DateTime MinDate = DateTime.MinValue; // × 非法(非基元类型) ``` - `readonly`:支持延迟初始化(可在构造函数中赋值) ```csharp public readonly string ConnectionString; public MyClass(string connStr) { ConnectionString = connStr; // √ 构造函数内赋值 } ``` 2. **值确定时机** - `const`值在编译时直接嵌入IL代码,修改后需重新编译所有引用程序集[^3]: ```csharp // 程序集A public const string VERSION = "1.0"; // 程序集B(引用A) Console.WriteLine(VERSION); // 编译后实际变为 Console.WriteLine("1.0") ``` - `readonly`值在运行时解析,修改后只需重新编译定义程序集[^4]。 3. **数据类型支持** - `const`仅支持: - 基元类型(`int`, `bool`, `double`等) - `string` `null` ```csharp public const UserRole DEFAULT_ROLE = UserRole.Guest; // × 非法(枚举非基元类型) ``` - `readonly`支持任意类型: ```csharp public readonly HttpClient _httpClient; // √ 支持引用类型 public readonly List<int> _ids = new(); // √ 支持集合 ``` 4. **使用场景对比** | 场景 | `const` 适用性 | `readonly` 适用性 | |--------------------------|----------------------|----------------------------| | 数学常数(π=3.14) | ✅ 最佳选择[^5] | ⚠️ 可用但非最佳 | | 依赖注入的对象 | × 不支持 | ✅ 推荐(构造函数初始化) | | 配置文件加载的值 | × 不支持 | ✅ 推荐 | | 跨程序集共享的常量 | ✅(但需注意重编译) | ⚠️ 可能引发版本问题 | --- #### 3. **代码示例对比** ```csharp public class Configuration { // const 示例(编译时常量) public const int MAX_RETRIES = 3; public const string DEFAULT_LANG = "en-US"; // readonly 示例(运行时常量) public readonly string ApiKey; public readonly DateTime ExpireTime = DateTime.Now.AddDays(30); public Configuration(string key) { ApiKey = key; // √ 构造函数内初始化readonly字段 // MAX_RETRIES = 5; // × 编译错误(const不可修改) } } ``` --- #### 4. **设计建议** 1. **优先使用`const`当**: - 值在编译时绝对确定(如数学常数、固定枚举值) - 需要零内存开销的高频访问场景 - 确保跨程序集的值一致性(接受重编译成本)[^5] 2. **优先使用`readonly`当**: - 值需运行时确定(如配置加载、依赖注入) - 使用非基元类型(自定义类、集合等) - 需要构造函数初始化逻辑[^3] 3. **避免陷阱**: - 不要用`const`存储可能变化的值(如版本号)[^5] - `readonly`引用类型字段的内容仍可修改(需额外保护) ```csharp public readonly List<int> Numbers = new(); Numbers.Add(1); // √ 合法(修改引用对象内容) // Numbers = new List(); // × 禁止重新赋值 ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值