欄位(field)是類中最常見的成員之一。C# 支援靜態欄位(型別欄位)和範例欄位,而上一節《C# const》中介紹的常數屬於特殊的靜態欄位。
對於範例欄位,其記憶體在建立範例時動態分配,而對於靜態欄位,其記憶體在型別物件建立時分配。
型別物件是型別載入到一個應用程式域(AppDomain) 時建立的。
唯讀欄位
由 readonly (唯讀)修飾的欄位只能在定義時或建構函式中賦值。
如果試圖在建構函式之外的地方修改唯讀欄位,就會發生編譯錯誤。
不過,反射可以修改唯讀欄位(實際上,沒有什麼是反射改不了的)。
唯讀欄位和常數很像,但也有如下區別:
-
常數兼有唯讀和靜態,所以常數一定是靜態的,但唯讀欄位不一定是。
-
唯讀欄位可以在執行時決定(例如在建構函式中賦值),而常數必須在定義時就賦值。
-
唯讀欄位可修飾任何型別,常數欄位雖然也可以修飾任何型別,但對於非基元型別, 它的值必須為 null,因此沒有什麼用。
假設直到執行時才知道值(例如通過讀取外部資料庫),並且希望之後值不會被改變,那麼唯讀欄位就很有用。
如果想將唯讀欄位設定為靜態的,而且也是直到執行時才知道值, 那麼需要使用靜態建構函式為其賦值。
注意,即使對於參照型別的唯讀欄位,我們也可以更改參照型別參照物件的值,但是, 我們不能更改參照型別的參照本身。
下面的程式碼中,我們試圖更改一個唯讀字串欄位的值,但是,更改字串的值,實質上是在字串池中新建一個字串,並將棧上原字串的變數指向堆中新的字串,即為更改參照。所以,這是不可以的。
class Program
{
static void Main(string[] args)
{
A a = new A();
a.print(); //0
//可以更改參照物件成員的值
a.field.b = 999;
a.print(); //999
//但是不可以更改參照本身
//a.field = new B();
//a.str = "hello";
Console.ReadKey();
}
}
class A
{
public readonly B field;
public readonly string str;
public A()
{
field = new B();
}
public void print()
{
Console.WriteLine(field.b);
}
}
class B
{
public double b;
}