const
用於修改欄位或局部變數的聲明。它指定欄位或局部變數的值是常數,不能被修改。常量的值必須在編譯的時候確定,編譯後,CLR將常量的值儲存在Assembly的怨資料中。如果變數是const,那麼他隱式的是static的。因此在聲明常數的時候只需將該變數聲明為const即可,而不允許在聲明常數的時候使用static。
當代碼引用常量時,CLR在中繼資料中尋找該符號,將提取的常量值嵌入到IL中,所以常量沒有地址以及相應的分配記憶體,而且不能通過引用傳遞變數
readonly
在欄位上使用的修飾符,表示該欄位是唯讀。當一個欄位在聲明為readonly的時候,只有兩種方式可以對其賦值,即作為聲明的一部分出現,或者在同一類的建構函式中。
對於執行個體欄位,在包含欄位聲明的類的執行個體建構函式中;或者,對於靜態欄位,在包含欄位聲明的類的靜態建構函式中。也只有在這些上下文中,將 readonly 欄位作為 out 或 ref 參數傳遞才有效。
static
聲明屬於類型本身而不是屬於特定對象的靜態成員,可用於類、欄位、方法、屬性、運算子、事件和建構函式,但不能用於索引器、解構函式或類以外的類型。
儘管類的執行個體包含該類所有執行個體欄位的單獨副本,但每個靜態欄位只有一個副本
不能通過類的執行個體引用靜態成員,只可以通過類型名稱引用它
如果對類應用 static 關鍵字,則該類的所有成員都必須是靜態
類(包括靜態類)可以有靜態建構函式。在程式開始和執行個體化類之間的某個時刻調用靜態建構函式
const和readonly
const
1. 在編譯期間解析的常量
2. 必須在聲明就初始化
3. 既可用來修飾類中的成員,也可修飾函數體內的局部變數。
readonly
1. 在運行期間解析的常量,
2. 既可以在聲明時初始化也可以在構造器中初始化,因此根據使用的建構函式,readonly的欄位可能具有不同的值。
3. 只可以用於修飾類中的成員
const和static readonly
都表示靜態常量,賦值以後都不可以更改
我們都知道,const和static readonly的確很像:通過類名而不是對象名進行訪問,在程式中唯讀等等。在多數情況下可以混用。
二者本質的區別在於,const的值是在編譯期間確定的,因此只能在聲明時通過常量運算式指定其值。而static readonly是在運行時計算出其值的,所以還可以通過靜態建構函式來賦值。
明白了這個本質區別,我們就不難看出下面的語句中static readonly和const能否互換了:
1. static readonly MyClass myins = new MyClass();
2. static readonly MyClass myins = null;
3. static readonly A = B * 20;
static readonly B = 10;
4. static readonly int [] constIntArray = new int[] {1, 2, 3};
5. void SomeFunction()
{
const int a = 10;
...
}
1:不可以換成const。new操作符是需要執行建構函式的,所以無法在編譯期間確定
2:可以換成const。我們也看到,Reference類型的常量(除了String)只能是Null。
3:可以換成const。我們可以在編譯期間很明確的說,A等於200。
4:不可以換成const。道理和1是一樣的,雖然看起來1,2,3的數組的確就是一個常量。
5:不可以換成readonly,readonly只能用來修飾類的field,不能修飾局部變數,也不能修飾property等其他類成員。
因此,對於那些本質上應該是常量,但是卻無法使用const來聲明的地方,可以使用static readonly。例如C#規範中給出的例子:
public class Color
{
public static readonly Color Black = new Color(0, 0, 0);
public static readonly Color White = new Color(255, 255, 255);
public static readonly Color Red = new Color(255, 0, 0);
public static readonly Color Green = new Color(0, 255, 0);
public static readonly Color Blue = new Color(0, 0, 255);static readonly需要注意的一個問題是,對於一個static readonly的Reference類型,只是被限定不能進行賦值(寫)操作而已。而對其成員的讀寫仍然是不受限制的。
public static readonly MyClass myins = new MyClass();
…
myins.SomeProperty = 10; //正常
myins = new MyClass(); //出錯,該對象是唯讀
但是,如果上例中的MyClass不是一個class而是一個struct,那麼後面的兩個語句就都會出錯。
private byte red, green, blue;
public Color(byte r, byte g, byte b)
{
red = r;
green = g;
blue = b;
}
}
C#擁有兩種不同的常量:
靜態常量(compile-time constants)和動
態常量(runtime constants)。它們有不同的特性,錯誤的使用不僅會損失效率,還可能造成錯誤。相比之下,靜態常量在速度上會稍稍快一些,但是靈活性卻比動態常量差很多。
//靜態常量(隱式是靜態)
public const int compiletimeConstant = 1;
//動態常量
public static readonly runtimeConstant = 1;
靜態常量在編譯時間會將其替換為所對應的值,也就是說下面這2句話通過編譯器編譯後產生的IL是一樣的。
//通過編譯後二者會被翻譯成相同的中繼語言
int myNum = compiletimeConstant;
int myNum = 1;
動態常量的值是在運行時獲得的。IL中將其標為唯讀常量,而不是用常量的值代替。
靜態常量只能被聲明為簡單的資料類型(內建的int和浮點型)、枚舉或字串。下面的程式段是通不過編譯的。你不能用new關鍵字初始化一個靜態常量,即便是對一個實值型別來說。
//這樣是錯誤的
public const DateTime myDateTime = new DateTime(2006,9,1,0,0,0);
//這樣是可以的
public static readonly DateTime myDateTime = new DateTime(2006,9,1,0,0,0);
唯讀資料也是常量的一種,它們不能在構造器初始化之後被修改。但是它同靜態常量不同,它的值是在運行時才被指派的,因此就會獲得更大的靈活性。動態常量可以是任意的資料類型。
二者最大的差別在於:靜態常量在編譯時間會將其換為對應的值,這就意味著對於不同的程式集來說,當你改變靜態常量的時候需要將其重新編譯,否則常量的值不會發生變化,可能引發潛在的問題,而動態常量就不會有這種情況。
用const定義的常量(隱式是靜態),需要像訪問靜態成員那樣去訪問const定義的常量,而用對象的成員方式去訪問會出編譯錯誤。 聲明的同時要設定常量值。
從另一方面來說,如果你的確要聲明一些從不改變且處處唯一的常量,例如鉤子函數SetWindowsHookEx的idHook參數或序列化時的版本等,就應該使用靜態常量。但是用到這樣的常量的機會不多。一般來說我們應該使用靈活性更高的動態常量。
靜態常量 動態常量
記憶體消耗 無 因為要儲存常量 有消耗
初始化 很少的簡單類型, 任意類型,可以在類建構函式中賦值
不能new,必須在
聲明同時賦值
何時發揮作用 編譯時間進行替換 相當於類中的資料成員