編寫高質量程式碼的50條黃金守則-Day 02(首選readonly而不是const)

位元飛發表於2020-08-14

編寫高質量程式碼的50條黃金守則-Day 02(首選readonly而不是const),本文由位元飛原創釋出,轉載務必在文章開頭附帶連結:https://www.byteflying.com/archives/6549

該系列文章由位元飛原創釋出,計劃用半年時間寫完全50篇文章,為大家提供編寫高質量程式碼的一般準則。

1、概述

眾所周知,.net 包含 2 種型別的常量,執行時常量和編譯時常量,它們的表現行為不同,使用不當,會使你陷入困境。雖然編譯時常量在執行上速度略快,但我依然強烈建議大家使用執行時常量(readonly),而不是編譯時常量(const)。在繼續深入瞭解之前,我們先要知道 .net 中兩種常量各自的特點。

2、.net中兩種常量的基本特點

我們看看以下程式碼片段:

編寫高質量程式碼的50條黃金守則-Day 02(首選readonly而不是const)

兩種常量的程式碼片段

我們很容易總結出它們各自的特點:

1、執行時常量 readonly 要麼在定義的時候賦初始值,要麼在建構函式中賦初始值;

2、編譯時常量 const 必須在定義的時候賦初始值。

那我為什麼建議大家使用執行時常量呢?因為編譯時常量可能會使你已發行程式的表現的和你測試時不同,這是為什麼呢?為了清楚的瞭解箇中原委,我們要明白編譯器為執行時常量和編譯時常量都做了什麼?

3、編譯器為readonly和const關鍵字做了什麼?

使用readonly修飾的常量為執行時常量,使用const修飾的常量為編譯時常量,我們先來看兩段示例程式碼:

public static readonly int ViewCount1 = 100;
public const int ViewCount2 = 100;

我們再來看一下兩段程式碼的IL:

編寫高質量程式碼的50條黃金守則-Day 02(首選readonly而不是const)

使用 DnSpy 解密的 IL 1

編寫高質量程式碼的50條黃金守則-Day 02(首選readonly而不是const)

使用 DnSpy 解密的 IL 2

編寫高質量程式碼的50條黃金守則-Day 02(首選readonly而不是const)

DnSpy 的反編譯結果

從上面的反編譯結果,可以明顯的看出 const 為編譯時常量,在編譯期間已經被寫到IL中。而 readonly 為執行時常量。Microsoft 技術支援文件中相關 IL 的解釋也可以印證這一點。

編寫高質量程式碼的50條黃金守則-Day 02(首選readonly而不是const)

Microsoft 技術支援文件中 ldsfld 的解釋

編寫高質量程式碼的50條黃金守則-Day 02(首選readonly而不是const)

Microsoft 技術支援文件中 Ldc_I4_S 的解釋

當然,對於這個案例,輸出的結果是一致的:

編寫高質量程式碼的50條黃金守則-Day 02(首選readonly而不是const)

ViewCount1 和 ViewCount2 的輸出結果

於是,我們得到以下重要結論:

1、const 和 readonly 在初始化一次後,不能再賦值;

2、const 在編譯時被編譯器所替換,而 readonly 在編譯時只做校驗,不作替換處理,執行時表現如同一般變數。

3、const 在 .net 中只能用於 數值型、字元型和 null,沒有例外。

我們再看一個簡單的示例:

public const int Birthday= 1986;
if(DateTime.UtcNow.Year == 1986)

if(DateTime.UtcNow.Year == Birthday)

由我們之前的分析可以得知,它們是完全相同的。然而,並不是所有的情況下,你都能得到你所期望的結果,這又是為什麼呢?

4、執行時所給出的結果,並不是你所期望的結果

某些情況下,執行時給出的結果與你所期望的結果大相徑庭。現在我們有以下場景,你有一個已交付的軟體,軟體包含一個可執行主程式 EffectiveCoding02.exe ,一個被主程式所引用的類庫 EffectiveCoding02.dll,主程式中包含以下程式碼:

public class SomeClass{

public static readonly int StartValue = 100;

public const int EndValue = 105;

}

類庫中包含以下程式碼:

for(var i = SomeClass.StartValue; i < SomeClass.EndValue; i++){

console.WriteLine($"value = {i}");

}

當初次交付程式給客戶的時候,以上程式碼完全按照你的預期執行。後來由於軟體迭代,你修改了主程式:

public class SomeClass{

public static readonly int StartValue = 110;

public const int EndValue = 115;

}

重新編譯後,因為你只更改了這個主程式,所以你將這個主程式交付給客戶,然而你卻得不到任何輸出內容,程式沒有按照你所預期的那樣工作,於是你會說出那句開發人員的至理名言,“我這裡是正常的啊!”。

產生這個問題的原因在於,由於編譯時常量 const 是在編譯時被編譯器所替換的,所以雖然你進行了編譯操作,然而使用者計算機中的類庫由於沒有被編譯器重新編譯,所以使用者計算機中的類庫中的 EndValue 還是上一次編譯時的結果,即105。然而執行時常量卻以正常的方式運作,所以它的值變成了110,自然,你得不到任何的輸出結果。因為迴圈初始值大於結束值。

5、總結

1、執行時常量 readonly 要麼在定義的時候賦初始值,要麼在建構函式中賦初始值;

2、編譯時常量 const 必須在定義的時候賦初始值;

3、const 和 readonly 在初始化一次後,不能再賦值;

4、const 在編譯時被編譯器所替換,而 readonly 在編譯時只做校驗,不作替換處理,執行時表現如同一般變數;

5、const 在 .net 中只能用於 數值型、字元型和 null,沒有例外;

6、儘量使用 readonly 常量,避免遇到已發行軟體無法按預期執行的問題。

開發人員應牢記以上開發守則,否則,人民群眾會仇恨你,你的朋友和家人也會嘲笑你、唾棄你

該系列文章由位元飛原創釋出,計劃用半年時間寫完全50篇文章,為大家提供編寫高質量程式碼的一般準則。

本文由 位元飛 原創釋出,歡迎大家踴躍轉載。

轉載請註明本文地址:https://www.byteflying.com/archives/6549

相關文章