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

