1.常量
常量是一個特殊的符號,它有一個不改變的值,定義常量時,它的值必須在編譯時間確定,確定後編譯器將常量的值儲存到程式集的中繼資料中。常量總是被視為靜態成員,而不是執行個體成員,定義常量將導致建立中繼資料。
這意味著只能為編譯器確定的基元類型定義常量。然後C#也允許定義一個非基元類型的常量變數,前提是把它的值設為null。
class Program
{
public const Program p1 = null;
public const Program p3 = new Program();//編譯出錯,只能用null對參考型別(字串除外)的常量欄位進行初始化
}
代碼引用一個常量符號時,編譯器會在定義常量的程式集的中繼資料中尋找該符號,然後提取常量的值並將值嵌入產生的IL代碼中,由於常量的值直接內嵌程式碼,所以運行時不會為常量分配任何記憶體,因此,不能擷取常量的地址,也不能以傳址的方式傳遞常量。
所以說常量沒有很好的跨程式集版本控制特性,只有在確定一個符號的值從不變化時,才使用常量。
如果希望在運行時從一個程式集中提取另一個程式集中的值,那麼不應該使用常量,而應該使用readonly欄位。
2.欄位
欄位是一種資料成員,容納了一個實值型別的執行個體,或者對一個參考型別的引用。
CLR支援類型欄位和執行個體欄位,對於類型欄位,用於容納欄位資料的動態記憶體是在類型對象中分配的。而類型對象是在類型載入到一個AppDomain時建立的,什麼時候載入到AppDomain呢?這通常是在引用了該類型的任何方法首次進行JIT編譯時間。對於執行個體欄位,用於容納欄位資料的動態記憶體則是在構造類型的一個執行個體時分配的。
CLR支援readonly欄位和read/write欄位,大多數欄位都是read/write的,代碼執行中欄位的值可以多次改變,但是readonly欄位只能在一個構造器方法中寫入。
internal class Manager
{
public readonly int d=200;
public Manager(int r)
{
//修改字讀欄位d,因為代碼在構造器中,所以允許這樣做。
this.d = r;
}
}
class Program
{
public const Program p=null;
static void Main()
{
Manager m = new Manager(400);
Console.WriteLine(m.d);//
}
當某個欄位時參考型別時,並且該欄位標記為readonly,那麼不可改變的是引用,而不是欄位引用的對象:
internal class Manager
{
public static readonly int[] n =new int[]{22,33,44,55,66};
public readonly int d=200;
public Manager(int r)
{
this.d = r;
}
}
class Program
{
public const Program p=null;
static void Main()
{
Manager.n[0] = 400;
Manager.n[1] = 800;
foreach (int p in Manager.n)
{
Console.WriteLine(p);
}//輸出400,800,44,55,66
}
}
許多欄位都是內聯初始化的(直接在代碼中賦值來初始化,而不是通過構造器。)C#允許使用這種方便的內聯初始化文法來初始化類的常量,欄位。但是C#實際是在構造器中對欄位進行初始化的,欄位的內聯初始化只不過是文法上的簡化而已。在C#中初始化一個欄位時,如果使用內聯文法,而不是在構造器中賦值,有一些效能問題需要考慮。