淺談C# 中 const 與 readonly的核心區(qū)別
在 C# 編程中,const 與 readonly 經常被統(tǒng)稱為“常量”,但二者在初始化規(guī)則、編譯/運行時行為、IL 生成方式、版本兼容性、引用類型語義等方面存在本質差異。誤用不僅可能引入隱蔽的邏輯錯誤,還會帶來庫升級后的版本陷阱。
一、初始化位置:編譯時強約束 vs 運行時一次性賦值
1??const:必須聲明即賦值(編譯期確定)
- 必須在聲明處賦值
- 值在編譯階段就已確定
- 任何位置都不能再次賦值(包括構造函數(shù))
public class ConstantDemo
{
public const int MaxRetryCount = 3;
public const string DefaultTitle = "C#常量解析";
// ? 編譯錯誤:聲明時未賦值
// public const double Pi;
// ? 編譯錯誤:不能在構造函數(shù)中修改
// public ConstantDemo()
// {
// MaxRetryCount = 5;
// }
}
?? 結論:const 是“聲明即終值”的編譯期常量。
2??readonly:聲明時或構造函數(shù)中賦值(運行期確定)
- 可以在聲明處賦值
- 也可以在 實例構造函數(shù) / 靜態(tài)構造函數(shù) 中賦值
- 每個字段只允許賦值一次
public class ReadonlyDemo
{
// 聲明時賦值
public readonly int MinAge = 18;
// 構造函數(shù)中賦值
public readonly int UserId;
public ReadonlyDemo(int userId)
{
UserId = userId;
}
// 靜態(tài) readonly:在靜態(tài)構造函數(shù)中賦值
public static readonly string Version;
static ReadonlyDemo()
{
Version = "1.0.1";
}
}
?? 結論:readonly 是“構造期凍結”的運行時常量。
二、修飾對象范圍:字段 + 局部變量 vs 僅字段
const:字段 & 局部變量都支持
public class ConstScopeDemo
{
public const int GlobalConst = 100;
public void LocalConstDemo()
{
const string LocalMsg = "局部常量";
Console.WriteLine(LocalMsg);
}
}
readonly:只能修飾字段
public class ReadonlyScopeDemo
{
public readonly int FieldReadonly = 50;
public void LocalReadonlyError()
{
// ? 編譯錯誤:readonly 不能修飾局部變量
// readonly int x = 10;
}
}
三、編譯期 vs 運行期:這是最本質的差異 ???
1??const:值被直接“內聯(lián)”到 IL 中
public const int ConstValue = 10;
public void UseConst()
{
int a = ConstValue;
}
IL 行為本質:
ldc.i4.s 10 // 直接壓棧常量 10
?? 重大隱患(版本陷阱):
- 修改類庫中的 const 值
- 但 引用方未重新編譯
- 引用方仍然使用舊值 ?
2??readonly:始終通過字段訪問(運行期綁定)
public readonly int ReadonlyValue;
public ReadonlyDemo()
{
ReadonlyValue = 20;
}
public void UseReadonly()
{
int b = ReadonlyValue;
}
IL 行為本質:
ldfld int32 ReadonlyValue
? 修改值后,只需重新編譯類庫即可,調用方無需重新編譯。
四、靜態(tài)語義:隱式靜態(tài) vs 顯式靜態(tài)
const:天然 static,且禁止顯式聲明
public class ConstStaticDemo
{
public const int ConstStatic = 10;
// ? 編譯錯誤
// public static const int Invalid = 20;
}
調用方式:
int x = ConstStaticDemo.ConstStatic;
readonly:默認實例級,靜態(tài)需顯式聲明
public class ReadonlyStaticDemo
{
public readonly int InstanceReadonly = 100;
public static readonly int StaticReadonly = 200;
}
五、引用類型語義:值不可變 vs 引用不可變
const:僅支持string/null
public class ConstReferenceDemo
{
public const string ConstString = "Hello";
public const object ConstNull = null;
// ? 編譯錯誤
// public const List<int> ConstList = new List<int>();
}
原因:
const需要 編譯期確定值- 除
string外,引用對象無法編譯期確定
readonly:支持任意引用類型(但僅鎖引用)
public class ReadonlyReferenceDemo
{
public readonly List<int> Numbers = new() { 1, 2, 3 };
public void Modify()
{
Numbers.Add(4); // ? 合法
// ? 編譯錯誤:不能重新賦值
// Numbers = new List<int>();
}
}
?? readonly ≠ 不可變對象
- 鎖的是 引用地址
- 不是對象內容
六、完整對比速查表
| 維度 | const | readonly |
|---|---|---|
| 初始化時機 | 編譯期 | 運行期 |
| 賦值位置 | 僅聲明處 | 聲明 / 構造函數(shù) |
| 修飾對象 | 字段 + 局部變量 | 僅字段 |
| 靜態(tài)特性 | 默認 static | 默認實例級 |
| IL 行為 | 內聯(lián)常量 | 字段訪問 |
| 引用類型 | string / null | 任意引用類型 |
| 版本安全 | ? 易出問題 | ? 安全 |
七、工程化使用建議(非常重要)
? 優(yōu)先使用const的場景
- 數(shù)學常量(
PI、E) - 永不變化的協(xié)議值、枚舉值
- 不會被類庫外部依賴引用的內部常量
public const int MaxDays = 7;
? 推薦使用readonly的場景(真實項目更常見)
- 類庫對外暴露的“常量”
- 配置讀取、構造參數(shù)注入
- 引用類型常量(集合、策略對象等)
public static readonly string ConnectionString;
static DbConfig()
{
ConnectionString = LoadFromConfig();
}
八、一句話記憶法(面試 & 實戰(zhàn))
const 是“編譯期寫死的字面量”
readonly 是“構造期凍結的字段”
結語
const 與 readonly 的差異,本質并不在“能不能改”,而在于:
- 值是在什么時候決定的?(編譯期 vs 運行期)
- 是否參與 IL 內聯(lián)?
- 是否影響程序集版本兼容?
在真實工程中:
?? 99% 對外暴露的“常量”,都應該使用 readonly 而不是 const。
理解這一點,你就已經超過了大多數(shù)只停留在語法層面的 C# 開發(fā)者。
到此這篇關于淺談C# 中 const 與 readonly的核心區(qū)別的文章就介紹到這了,更多相關C# const readonly內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
C#版的 Escape() 和 Unescape() 函數(shù)分享
從網上看到兩個方法, C# 版的 Escape() 和 Unescape(),收藏下。2011-05-05
C#使用ADO.Net部件來訪問Access數(shù)據(jù)庫的方法
數(shù)據(jù)庫的訪問是所有編程語言中最重要的部分,C#提供了ADO.Net部件用于對數(shù)據(jù)庫進行訪問。本文從最簡單易用的微軟Access數(shù)據(jù)庫入手討論在C#中對數(shù)據(jù)庫的訪問。2015-09-09
C#中winform窗體實現(xiàn)注冊/登錄功能實例(DBHelper類)
在編寫項目時,編寫了一部分關于登錄頁面的一些代碼,下面這篇文章主要給大家介紹了關于C#中winform窗體實現(xiàn)注冊/登錄功能(DBHelper類)的相關資料,文中通過圖文介紹的非常詳細,需要的朋友可以參考下2023-06-06

