文章目錄
C#中有兩種常量類型,分別為readonly(運行時常量)與const(編譯時間常量),本文將就這兩種類型的不同特性進行比較並說明各自的適用情境。
工作原理
readonly為運行時常量,程式運行時進行賦值,賦值完成後便無法更改,因此也有人稱其為唯讀變數。
const為編譯時間常量,程式編譯時間將對常量值進行解析,並將所有常量引用替換為相應值。
下面聲明兩個常量:public static readonly int A = 2; //A為運行時常量
public const int B = 3; //B為編譯時間常量
下面的運算式:
int C = A + B;
經過編譯後與下面的形式等價:
int C = A + 3;
可以看到,其中的const常量B被替換成字面量3,而readonly常量A則保持引用方式。
聲明及初始化
readonly常量只能聲明為類欄位,支援執行個體類型或靜態類型,可以在聲明的同時初始化或者在建構函式中進行初始化,初始化完成後便無法更改。
const常量除了可以聲明為類欄位之外,還可以聲明為方法中的局部常量,預設為靜態類型(無需用static修飾,否則將導致編譯錯誤),但必須在聲明的同時完成初始化。
資料類型支援
由於const常量在編譯時間將被替換為字面量,使得其取實值型別受到了一定限制。const常量只能被賦予數字(整數、浮點數)、字串以及枚舉類型。下面的代碼無法通過編譯:public const DateTime D = DateTime.MinValue;
改成readonly就可以正常編譯:
public readonly DateTime D = DateTime.MinValue;
可維護性
readonly以引用方式進行工作,某個常量更新後,所有引用該常量的地方均能得到更新後的值。
const的情況要稍稍複雜些,特別是跨程式集調用:public class Class1
{
public static readonly int A = 2; //A為運行時常量
public const int B = 3; //B為編譯時間常量
}
public class Class2
{
public static int C = Class1.A + Class1.B; //變數C的值為A、B之和
}
Console.WriteLine(Class2.C); //輸出"5"
假設Class1與Class2位於兩個不同的程式集,現在更改Class1中的常量值:
public class Class1
{
public static readonly int A = 4; //A為運行時常量
public const int B = 5; //B為編譯時間常量
}
編譯Class1並部署(注意:這時並沒有重新編譯Class2),再次查看變數C的值:
Console.WriteLine(Class2.C); //輸出"7"
結果可能有點出乎意料,讓我們來仔細觀察變數C的賦值運算式:
public static int C = Class1.A + Class1.B;
編譯後與下面的形式等價:
public static int C = Class1.A + 3;
因此不管常量B的值如何變,對最終結果都不會產生影響。雖說重新編譯Class2即可解決這個問題,但至少讓我們看到了const可能帶來的維護問題。
效能比較
const直接以字面量形式參與運算,效能要略高於readonly,但對於一般應用而言,這種效能上的差別可以說是微乎其微。
適用情境
在下面兩種情況下:
a.取值永久不變(比如圓周率、一天包含的小時數、地球的半徑等)
b.對程式效能要求非常苛刻
可以使用const常量,除此之外的其他情況都應該優先採用readonly常量。
Tag標籤: c#,.net
Feedback#1樓 回複 引用
2008-05-26 22:40 by C#愛好者 [未註冊使用者] 正好,學學。謝謝了。#2樓 回複 引用
2008-05-26 22:40 by 牛 牛 [未註冊使用者] 講的都是些基礎知識了,呵呵學習鞏固了。 #3樓 回複 引用 查看
2008-05-26 22:50 by StephenJu 怎麼園子裡有的說readonly是定義唯讀變數,也有的說是定義常量?到底是什麼啊?
請問Class1與Class2位於兩個不同的程式集,現在更改Class1中的常量值後,輸出結果怎麼是7?? #4樓 回複 引用 查看
2008-05-27 01:30 by 水言木 @StephenJu
我覺得叫“唯讀變數”會好點吧,其實大家的意思都是一個。
嚴格來說,常量是必須在聲明時賦值且不能再次賦值的,但readonly卻不是,它可以在建構函式中對其再次賦值,另外常量的值在編譯時間就確定。#5樓 回複 引用
2008-05-27 08:17 by 風中之羽 [未註冊使用者] const 的概念就是一個包含不能修改的值的變數。
常數運算式是在編譯時間可被完全計算的運算式。因此不能從一個變數中提取的值來初始化常量。
如果 const int a = b+1;b是一個變數,顯然不能再編譯時間就計算出結果,所以常量是不可以用變數來初始化的。
readonly 允許把一個欄位設定成常量,但可以執行一些運算,可以確定它的初始值。
因為 readonly 是在計算時執行的,當然它可以用某些變數初始化。
readonly 是執行個體成員,所以不同的執行個體可以有不同的常量值,這使readonly更靈活。
readonly 關鍵字與 const 關鍵字不同。
1. const 欄位只能在該欄位的聲明中初始化。
readonly 欄位可以在聲明或建構函式中初始化。因此,根據所使用的建構函式,readonly 欄位可能具有不同的值。
2. const 欄位是編譯時間常數,而 readonly 欄位可用於運行時常數。
3. const 預設就是靜態,而 readonly 如果設定成靜態就必須顯示聲明。
4.const 對於參考型別的常數,可能的值只能是 string 和 null。
readonly可以是任何類型
* 需要注意的一個問題是:
對於一個 readonly 的 Reference 類型,只是被限定不能進行賦值(寫)操作而已。而對其成員的讀寫仍然是不受限制的。
public static readonly Class1 my = new Class1();
…
my.SomeProperty = 10; //正常
my = new Class1(); //出錯,該對象是唯讀
但是,如果上例中的 Class1 不是一個 Class 而是一個 struct,那麼後面的兩個語句就都會出錯。
static readonly:
Java 中 static 是當載入一個類時執行一次的。
C#中是怎麼執行的,我沒有查到。很奇怪幾乎每本java的書都會說static的問題,C#的往往只說怎麼用,但是應該是在main函數調用之前初始化,所以static readonly也是運行時的,可以用變數付值,如:
private static readonly string path = System.Windows.Forms.Application.StartupPath + “aaa”;
--摘自LTP.NET知識庫系列
#6樓 回複 引用 查看
2008-05-27 08:18 by zmxmiss 學習 學習#7樓 回複 引用 查看
2008-05-27 08:21 by Da Vinci Effective C#
規則2: 盡量使用readonly而不是const
只是readonly的效能較差
看情況用了#8樓 回複 引用 查看
2008-05-27 08:22 by 81 寫的好,不同assembly中對const的處理這個問題,以前的確沒有注意到。#9樓 回複 引用 查看
2008-05-27 08:26 by Da Vinci @StephenJu
readonly是動態常量, const是靜態常量
用readonly定義的常量需要分配記憶體
4+3 = 7#10樓 回複 引用 查看
2008-05-27 08:27 by Da Vinci @81
所以用const有可能會出錯#11樓 回複 引用 查看
2008-05-27 08:43 by CoderZh 很好#12樓 回複 引用 查看
2008-05-27 09:57 by StephenJu @水言木
@Da Vinci
非常感謝,現在明白了,呵呵!#13樓 回複 引用 查看
2008-05-27 09:59 by StephenJu @風中之羽
非常感謝!#14樓 回複 引用 查看
2008-05-27 11:31 by OOP 恩,寫的非常好。#15樓 回複 引用
2008-05-27 11:31 by wxc [未註冊使用者] 補充 風中之羽:
const 既可以修飾類中的成員,也可以修飾函數體中的局部變數。
而readonly 只能修飾類中的成員#16樓 回複 引用 查看
2008-05-27 12:59 by Anytao 寫得很好。
不過我覺得不夠全面,其實static readonly和const更具可比性,在應用範圍上可以更加具體一些,例如readonly不能修飾局部變數,而const可以。。。
另外,“readonly為運行時常量,程式運行時進行賦值,一旦賦值便無法更改”的說法不夠精確,實際上readonly的值是可以更改的,在建構函式內就可以變更。
所以,其實從記憶體角度理解可以看得更加透徹:-)
謝謝你的妙文:-) #17樓 回複 引用 查看
2008-05-27 13:02 by Anytao l另外,對於唯讀特性而言,我覺得通過屬性的set和get訪問器來實現可讀可寫對於封裝性而言,有著更加優雅的代碼體驗和可靠性:-)#18樓 [樓主] 回複 引用 查看
2008-05-27 13:36 by Kenny@Web @Anytao
謝謝你的建議,已對內容進行補充:-)#19樓 回複 引用 查看
2008-05-27 15:51 by 求知無傲 晚上看#20樓 回複 引用
2008-05-27 17:06 by Train-i [未註冊使用者] 說的很詳細啊#21樓 回複 引用 查看
2008-05-27 19:35 by 曲濱*銘龘鶽 樓主不要誤導小朋友啊;
readonly 絕對不是常量的先說;
這東西叫“唯讀變數”;
只不過看似比較類似常量,的一個偽常量;
C# 代碼
public class SampleClass
{
const int C = 25;
readonly int R = 25;
public void Test()
{
Console.WriteLine(C);
Console.WriteLine(R);
}
}
編譯後
public class SampleClass
{
private const int C = 0x19;
private readonly int R = 0x19;
public void Test()
{
Console.WriteLine(0x19);
Console.WriteLine(this.R);
}
}
簡單的說“常量”是在編譯時間直接放在代碼裡的,並不是指向某個變數。
#22樓 回複 引用
2008-05-27 20:36 by James Dai [未註冊使用者] static readonly是擷取地址,
const是直接擷取值.類似於淺拷貝與深拷貝#23樓 回複 引用
2008-08-24 09:51 by lonely_c# [未註冊使用者] 原來學習C#的這麼多啊