C#欄位(field)

2020-07-16 10:04:45
欄位(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;
}