儘管你寫了很多年的C#的代碼,但是可能當別人問到你const與readonly的區別時候,還是會小小的愣一會吧~
筆者也是在看歐立奇版的《.Net 程式員面試寶典》的時候,才發現自己長久以來竟然在弄不清出兩者的情況下,混用了這麼長的時間。的確,const與readonly 很像,都是將變數聲明為唯讀,且在變數初始化後就不可改寫。那麼,const與readonly 這兩個修飾符到底區別在什麼地方呢?其實,這個牽扯出C#語言中兩種不同的常量類型:靜態常量(compile-time constants)和動態常量(runtime constants)。這兩者具有不同的特性,錯誤的使用不僅會損失效率,而且還會造成錯誤。
首先先解釋下什麼是靜態常量以及什麼是動態常量。靜態常量是指編譯器在編譯時間候會對常量進行解析,並將常量的值替換成初始化的那個值。而動態常量的值則是在啟動並執行那一刻才獲得的,編譯器編譯期間將其標示為唯讀常量,而不用常量的值代替,這樣動態常量不必在聲明的時候就初始化,而可以延遲到建構函式中初始化。
當你大致瞭解上面的兩個概念的時候,那麼就可以來說明const與readonly了。const修飾的常量是上述中的第一種,即靜態常量;而readonly則是第二種,即動態常量。那麼區別可以通過靜態常量與動態常量的特性來說明:
1)const修飾的常量在聲明的時候必須初始化;readonly修飾的常量則可以延遲到建構函式初始化
2)const修飾的常量在編譯期間就被解析,即常量值被替換成初始化的值;readonly修飾的常量則延遲到啟動並執行時候
此外const常量既可以聲明在類中也可以在函數體內,但是static readonly常量只能聲明在類中。
可能通過上述純概念性的講解,對有些初學者有些暈乎。下面就一些例子來說明下:
using System;
class P
{
static readonly int A=B*10;
static readonly int B=10;
public static void Main(string[] args)
{
Console.WriteLine("A is {0},B is {1} ",A,B);
}
}
對於上述代碼,輸出結果是多少?很多人會認為是A is 100,B is 10吧!其實,正確的輸出結果是A is 0,B is 10。好吧,如果改成下面的話:
using System;
class P
{
const int A=B*10;
const int B=10;
public static void Main(string[] args)
{
Console.WriteLine("A is {0},B is {1} ",A,B);
}
}
對於上述代碼,輸出結果又是多少呢?難道是A is 0,B is 10?其實又錯了,這次正確的輸出結果是A is 100,B is 10。
那麼為什麼是這樣的呢?其實在上面說了,const是靜態常量,所以在編譯的時候就將A與B的值確定下來了(即B變數時10,而A=B*10=10*10=100),那麼Main函數中的輸出當然是A is 100,B is 10啦。而static readonly則是動態常量,變數的值在編譯期間不予以解析,所以開始都是預設值,像A與B都是int類型,故都是0。而在程式執行到A=B*10;所以A=0*10=0,程式接著執行到B=10這句時候,才會真正的B的初值10賦給B。如果,你還是不大清楚的話,我們可以藉助於微軟提供的ILDASM工具,只需在Vs 2008 Command下輸入ILDASM就可以開啟,如下所示:
分別開啟上述兩個代碼編譯後產生的可執行檔,如所示:
static readonly可執行程式的結構 const可執行程式的結構
在上述兩張圖中都可以看到A與B常量,分別雙擊節點可以看出其中的差異:
static readonly修飾的常量A const修飾的常量A
static readonly修飾的常量B const修飾的常量B
從中可以看出,const修飾的常量在編譯期間便已將A,B的字面值算出來了,而static readonly修飾的常量則未解析,所以在Main函數中有以下的區別:
static readonly程式的Main函數 const程式的Main函數
從Main函數中我們可以看出,const的那個程式的輸出直接是100與10,而readonly在輸出的時候確實P::A與P::B,即將A與B常量的值延遲到啟動並執行時候才去確定,故輸出是0與10。
那麼對於靜態常量以及動態常量還有什麼特性呢?其實,靜態常量只能被聲明為簡單的資料類型(int以及浮點型)、枚舉、布爾或者字串型,而動態常量則除了這些類型,還可以修飾一些物件類型。如DateTime類型,如下:
//錯誤
const DateTime time=new DateTime();
//正確
static readonly DateTime time=new DateTime();
上述錯誤在於不能使用new關鍵字初始化一個靜態常量,即便是一個實值型別,因為new將會導致到運行時才能確定值,與靜態變數編譯時間就確定字面值有悖。
歐書上最後給出了對靜態常量與動態常量之間的比較,如下表所示:
最近隨筆較少,希望這篇能給一些朋友帶來一些協助~