c#的const和readonly使用小結

來源:互聯網
上載者:User

前言:從上周開始看<<CLR via C#>>第三版,剛剛看完了第四和第七兩章,這兩章都算常識,但是有很多基礎知識和術語理解不是很深入,所以看得有點慢,計劃每兩周寫點心得,是以成文。

1、應用情境重現

一個簡單的應用程式解決方案,如下:

其中,類庫CSharpLib裡定義一個簡單的類SomeType如下:

namespace CSharpLib{    public class SomeType    {        public const int ConstField = 50;        public static readonly int ReadonlyField = 50;    }}

在控制台應用程式ConsoleApp中,引用類庫CSharpLib,然後寫下如下代碼:

using System;namespace ConsoleApp{    using CSharpLib;    class Program    {        static void Main(string[] args)        {            Console.WriteLine("Const field is {0}.", SomeType.ConstField);            Console.WriteLine("Readonly field is {0}.", SomeType.ReadonlyField);            Console.ReadKey();        }    }}

這樣這個控制台應用程式的輸出就都是50,這個結果應該是每個開發人員都預期的,沒有任何可疑之處。

當我們把類庫CSharpLib中的常量都改變時:

namespace CSharpLib{    public class SomeType    {        public const int ConstField = 55;        public static readonly int ReadonlyField = 5555;    }}

也就是說,我們預期的目標輸出是55和5555。當改好類庫,開發人員發現控制台應用程式沒做出任何修改,所以只將類庫重建,然後將.dll檔案拷到控制台應用程式bin目錄下(做過asp.net開發的應該都知道,當某個類庫修改時,我們通常都直接拷貝修改後的類庫.dll到網站下的bin目錄,除了習慣性偷懶,版本控制也是一個問題),然後雙擊運行ConsoleApp.exe檔案,我們驚訝地發現當前的輸出不是55和5555,而是50和5555,也就是說,const欄位修改後沒有達到預期目的。

 

2、注意點總結

為什麼會發生上面的現象呢?從c#的常量的本質說起。

(1)、什麼是常量(書中原話摘錄)

“常量(constant)是一個特殊的符號,它是一個從不變化的值。定義常量符號時,它的值必須能在編譯時間確定。確定之後,編譯器將常量的值儲存到程式集的中繼資料中。常量總是被視為靜態成員,而不是執行個體成員。代碼引用一個常量符號時,編譯器會在定義常量的程式集的中繼資料中尋找該符號,提取常量的值,並將值嵌入產生的IL代碼中。由於常量的值直接內嵌程式碼,所以在運行時,不需要為常量分配記憶體。除此之外,不能擷取常量的地址,也不能以傳引用的方式傳遞常量。這些限制同時意味著,常量沒有很好的跨程式集版本控制特性。“

(2)、靜態常量和動態常量

簡單來說,用const修飾的就是靜態常量(compile-time constants),而且它是隱式靜態。動態常量(runtime constants)的值是在運行時獲得的,IL中將其標為唯讀常量,而不是用常量的值代替。

下面對二者的特點進行簡單比較:

 

靜態常量

動態常量

記憶體配置

可使用類型

較少的C#基元類型,如String,Int32等等

任意類型

初始化賦值 聲明變數時同時賦值 可在建構函式中賦值
何時發揮作用 編譯時間進行替換,可產生中繼資料 相當於類中的資料成員

通過上述分析,我們可以理解1中所產生的輸出不一致現象,是因為const欄位ConstField已經嵌入到控制台應用程式的IL代碼中(擷取ConstField欄位並不從類庫的dll檔案中載入),從IL中我們可以看得很清楚:

.method private hidebysig static void  Main(string[] args) cil managed{  .entrypoint  // 代碼大小       47 (0x2f)  .maxstack  8  IL_0000:  nop  IL_0001:  ldstr      "Const field is {0}."  IL_0006:  ldc.i4.s   50  //const欄位沒有變化  IL_0008:  box        [mscorlib]System.Int32  IL_000d:  call       void [mscorlib]System.Console::WriteLine(string,                                                                object)  IL_0012:  nop  IL_0013:  ldstr      "Readonly field is {0}."  IL_0018:  ldsfld     int32 [CSharpLib]CSharpLib.SomeType::ReadonlyField  IL_001d:  box        [mscorlib]System.Int32  IL_0022:  call       void [mscorlib]System.Console::WriteLine(string,                                                                object)  IL_0027:  nop  IL_0028:  call       valuetype [mscorlib]System.ConsoleKeyInfo [mscorlib]System.Console::ReadKey()  IL_002d:  pop  IL_002e:  ret} // end of method Program::Main

而不是像static readonly欄位一樣從記憶體載入類庫程式集。如果應用程式需擷取新值,也必須重新編譯,或者使用readonly欄位。

 

3、類比問題

我記得以前看過的一篇博文,是討論枚舉使用中輸出不一致情況的,google了一下。您可以參考這一篇小心枚舉陷阱進行比較,如果您對IL有瞭解,可以簡單從IL著手分析對比,更能加深理解。

 

總結:const和readonly在編程實踐中經常用到,理解它們的基本原理,對於開發和維護都是非常有必要的。在類庫開發定義常量的時候,個人偏向於使用static readonly組合關鍵字。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.