c# 中的常量有兩種,分別是編譯期常量和運行期常量。通過名字我們就可以看出來它倆在行爲上是不同的。在開發中如果這兩種常量選擇的不合適,就會影響到程序的開發工作以及程序的性能。下面我們先來看一下運行期常量和編譯期常量的定義方法。
零、定義
運行期常量我們使用 readonly 來定義,而編譯器常量我們使用 const 來定義。
// 運行期常量
public static readonly string name = "張三" ;
//編譯期常量
public const int age = 20 ;
一、運行期常量和編譯期常量
- 編譯期常量
編譯期常量可以使程序運行速度變得快一些,但是它沒有運行期常量靈活,一般我們會在特別關注程序性能,並且常量取值不會隨版本的變化而變化的情況下才會使用到它。這種常量與直接使用字面量的寫法在編譯爲 MSIL 後的結果是一樣的。例如if( userAge == age )
等價於if( userAge == 20 )
。
這裏有幾點很重要的需要注意:
- 編譯期常量只能用內置的整數、浮點數、枚舉、字符串或 null 來進行初始化和賦值,在生成 MSIL 的過程中只有這些原始類型的編譯期常量纔會被替換成字面量;
- 編譯期常量可以在方法內部聲明;
- 編譯期常量是靜態常量;
- 在另一個程序集中調用靜態常量會導致不兼容問題(這個問題將在案例小節中講解)。
- 運行期常量
在開發的大部分情況下我們使用的是運行期常量,這種常量靈活性強,幾乎可以支持所有的類型。它不僅可以在聲明的時候直接初始化,也可以在構造器中初始化。運行期常量所生成的 MSIL 會通過引用的方式來使用這個常量。
同樣這裏有幾點需要注意:
- 運行期常量可以用來聲明實例級別的常量,給同一個實例設定不同的常量值;
- 運行期常量是在程序運行時纔會被解析。
二、案例
下面我們來看一個案例:
namespace readonly_and_const
{
public class main
{
public static readonly string name = "張三";
public const int age = 20;
}
}
namespace Test
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine(readonly_and_const.main.age);
Console.WriteLine(readonly_and_const.main.name);
Console.Read();
}
}
}
上述代碼中我們創建了兩個程序集,Test 程序集引用了 readonly_and_const 程序集。我們先行一下代碼,來看一下運行結果:
下面我們將程序集 readonly_and_const 中的 age 和 name 都進行修改並運行:
namespace readonly_and_const
{
public class main
{
public static readonly string name = "jack";
public const int age = 25;
}
}
運行結果如下:
我們從運行結果可以看到,name 的值已經改變,但是 age 的值沒有改變,這是因爲在編譯 Test 程序集時編譯器直接寫入了 20 這個字面量,而不是去引用放置 age 的那個空間。但是 name 因爲時運行期常量,因此會在運行時去應用放置了 name 的那個空間,因此輸出了正確的值。解決這個問題有兩種方法,一種是將 age 修改爲運行期常量,另一種是重新編譯 Test 程序集。具體應該使用哪個方法解決,應該視程序而定。
Tip:修改訪問級別爲 public 的 const 常量時調用它的程序集必須重新編譯,因爲這種修改相當於修改接口。但是修改 public 級別的 readonly 常量相當於修改細節實現,