C#基本知識點-Readonly和Const的區別

什麼是靜態常量(Const)和動態常量(Readonly)函數

 
  先解釋下什麼是靜態常量(Const)以及什麼是動態常量(Readonly)。
  靜態常量(Const)是指編譯器在編譯時候會對常量進行解析,並將常量的值替換成初始化的那個值。
  動態常量(Readonly)的值則是在運行的那一刻纔得到的,編譯器編譯期間將其標示爲只讀常量,而不用常量的值代替,這樣動態常量沒必要在聲明的時候就初始化,而能夠延遲到構造函數中初始化。
 
靜態常量(Const)和動態常量(Readonly)之間的區別
 
 
 
靜態常量(Compile-time Constant)
 
動態常量(Runtime Constant)
 
定義
 
聲明的同時要設置常量值。
 
聲明的時候能夠不須要進行設置常量值,能夠在類的構造函數中進行設置。
 
類型限制
 
只能修飾基元類型,枚舉類型或者字符串類型。
 
沒有限制,能夠用它定義任何類型的常量。
 
對於類對象而言
 
對於全部類的對象而言,常量的值是同樣的。
 
對於類的不一樣對象而言,常量的值能夠是不同的。
 
內存消耗
 
無。
 
要分配內存,保存常量實體。
 
綜述
 
性能要略高,無內存開銷,可是限制頗多,不靈活。
 
靈活,方便,可是性能略低,且有內存開銷。
 
Const修飾的常量在聲明的時候必須初始化;Readonly修飾的常量則能夠延遲到構造函數初始化 。
Const常量既能夠聲明在類中也能夠在函數體內,可是Static Readonly常量只能聲明在類中。Const是靜態常量,因此它自己就是Static的,所以不能手動再爲Const增長一個Static修飾符。
Const修飾的常量在編譯期間就被解析,即:通過編譯器編譯後,咱們都在代碼中引用Const變量的地方會用Const變量所對應的實際值來代替; Readonly修飾的常量則延遲到運行的時候。
  舉個例子來講明一下:
 
 
 
        public static readonly int NumberA = NumberB * 10;
        public static readonly int NumberB = 10;
 
        public const int NumberC = NumberD*10;
        public const int NumberD = 10;
 
        static void Main(string[] args)
        {
            Console.WriteLine("NumberA is {0}, NumberB is {1}.", NumberA, NumberB);//NumberA is 0, NumberB is 10.
            Console.WriteLine("NumberC is {0}, NumberD is {1}.", NumberC, NumberD);//NumberC is 100, NumberD is 10.
            Console.ReadKey();
        }
 
  以上是語法方面的應用,那在實際的用法上,仍是有些微妙的變化,一般不易發覺.
  舉個例子來講明一下:
  在程序集DoTestConst.dll 中有一個類MyClass,定義了一個公開的靜態變量Count
 
    public static class MyClass
    {
        public const int Count = 10;
    }
  而後另一個應用程序中引用DoTestConst.dll,並在代碼中做以下調用:
 
    public static void Main(string[] args)
    {
        Console.WriteLine(DoTestConst.MyClass.Count);//輸出10
        Console.ReadKey();
    }
  毫無疑問,很是簡單的代碼,直接輸出10。
  接下來更新MyClass的Count的值爲20,而後從新編譯DoTestConst.dll,並更新到應用程序的所在目錄中,注意不要編譯應用程序。那麼這時候的輸出結果按預期那麼想應該是20纔對,但實際上仍是10,爲何呢?
  這就是Const的特別之處,有多特別仍是直接看生成的IL,查看IL代碼(假設這時候Count的值爲10)
 
  IL_0000: nop
  IL_0001: ldc.i4.s 10
  IL_0003: call void [mscorlib]System.Console::WriteLine(int32)
 
  紅色代碼很明顯的代表了,直接加載10,沒有經過任何類型的加載而後獲得對應變量的,也就是說在運行時沒有去加載DoTestConst.dll,那麼是否意味着沒有DoTestConst.dll也能夠運行呢?答案是確定的,刪除DoTestConst.dll也能夠運行,是否很詭異呢?也就解釋了以前的實驗,爲何更新Const變量的值以後沒有調用新的值,由於程序在運行的時候根本不會去加載DoTestConst.dll。那麼10這個值是從哪來的呢?實際上CLR對於Const變量作了特殊處理,是將Const的值直接嵌入在生成的IL代碼中,在執行的時候不會再去從dll加載。這也帶來了一個不容易發覺的Bug,所以在引用其餘程序集的Const變量時,需考慮到版本更新問題,要解決這個問題就是把調用的應用程序再編譯一次就ok了。但實際程序部署更新時可能只更新個別文件,這時候就必須用Readonly關鍵字來解決這個問題。
 
  接下來看Readonly的版本:
 
    public static class MyClass
    {
        public static readonly int Count = 10;
    }
  調用方代碼不變,接着看生成的IL代碼:
 
  IL_0000: nop
  IL_0001: ldsfld int32 [DoTestConst]DoTestConst.MyClass::Count
  IL_0006: call void [mscorlib]System.Console::WriteLine(int32)
 
  很明顯加載代碼變了,一個很常見的ldsfld動做,請求了DoTestConst.MyClass的Count變量,是經過強制要求加載DoTestConst來實現的。所以這時候更新Count的值從新編譯以後,仍是不編譯調用程序,而後再執行就會看到新的值。而這時候若是刪除DoTestConst.dll那麼,會出現找不到dll之類的異常。這也充分說明了對於Readonly定義的變量是在運行時加載的。
 
動態常量(Readonly)被賦值後不能夠改變
 
  ReadOnly 變量是運行時變量,它在運行時第一次賦值後將不能夠改變。其中「不能夠改變」分爲兩層意思:
 
對於值類型變量,值自己不能夠改變(Readonly, 只讀)
對於引用類型變量,引用自己(至關於指針)不可改變。
  值類型變量,舉個例子說明一下:
 
    public class Student
    {
        public readonly int Age;
 
        public Student(int age)
        {
            this.Age = age;
        }
    }
 
  Student的實例Age在構造函數中被賦值之後就不能夠改變,下面的代碼不會編譯經過:
 
Student student = new Student(20);
student.Age = 21; //錯誤信息:沒法對只讀的字段賦值(構造函數或變量初始化器中除外)
  引用類型變量,舉個例子說明一下:
 
 
    public class Student
    {
        public int Age; //注意這裏的Age是沒有readonly修飾符的
 
        public Student(int age)
        {
            this.Age = age;
        }
    }
 
    public class School
    {
        public readonly Student Student;
 
        public School(Student student)
        {
            this.Student = student;
        }
    }
 
  School實例的Student是一個引用類型的變量,賦值後,變量不能再指向其餘任何的Student實例,因此,下面的代碼將不會編譯經過:
 
School school = new School(new Student(10));
school.Student = new Student(20);//錯誤信息:沒法對只讀的字段賦值(構造函數或變量初始化器中除外)
  引用自己不能夠改變,可是引用說指向的實例的值是能夠改變的。因此下面的代碼是能夠編譯經過的:
 
School school = new School(new Student(10));
school.Student.Age = 20;
  在構造方法中,咱們能夠屢次對Readonly修飾的常量賦值。舉個例子說明一下:
 
 
    public class Student
    {
        public readonly int Age = 20;//注意:初始化器其實是構造方法的一部分,它實際上是一個語法糖
 
        public Student(int age)
        {
            this.Age = age;
            this.Age = 25;
            this.Age = 30;
        }
    }
 
總結
 
  Const和Readonly的最大區別(除語法外)
  Const的變量是嵌入在IL代碼中,編譯時就加載好,不依賴外部dll(這也是爲何不能在構造方法中賦值)。Const在程序集更新時容易產生版本不一致的狀況。
Readonly的變量是在運行時加載,需請求加載dll,每次都獲取最新的值。Readonly賦值引用類型之後,引用自己不能夠改變,可是引用所指向的實例的值是能夠改變的。在構造方法中,咱們能夠屢次對Readonly賦值。
相關文章
相關標籤/搜索