程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> C#基本知識點-Readonly和Const的區別

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

編輯:C#入門知識

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賦值。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved