C#中有兩種常量類型,分別為readonly(運(yùn)行時(shí)常量)與const(編譯時(shí)常量),本文將就這兩種類型的不同特性進(jìn)行比較并說明各自的適用場景。
工作原理
readonly為運(yùn)行時(shí)常量,程序運(yùn)行時(shí)進(jìn)行賦值,賦值完成后便無法更改,因此也有人稱其為只讀變量。
const為編譯時(shí)常量,程序編譯時(shí)將對(duì)常量值進(jìn)行解析,并將所有常量引用替換為相應(yīng)值。
下面聲明兩個(gè)常量:
public static readonly int A = 2; //A為運(yùn)行時(shí)常量
public const int B = 3; //B為編譯時(shí)常量
下面的表達(dá)式:
int C = A + B;
經(jīng)過編譯后與下面的形式等價(jià):
int C = A + 3;
可以看到,其中的const常量B被替換成字面量3,而readonly常量A則保持引用方式。
聲明及初始化
readonly常量只能聲明為類字段,支持實(shí)例類型或靜態(tài)類型,可以在聲明的同時(shí)初始化或者在構(gòu)造函數(shù)中進(jìn)行初始化,初始化完成后便無法更改。
const常量除了可以聲明為類字段之外,還可以聲明為方法中的局部常量,默認(rèn)為靜態(tài)類型(無需用static修飾,否則將導(dǎo)致編譯錯(cuò)誤),但必須在聲明的同時(shí)完成初始化。
數(shù)據(jù)類型支持
由于const常量在編譯時(shí)將被替換為字面量,使得其取值類型受到了一定限制。const常量只能被賦予數(shù)字(整數(shù)、浮點(diǎn)數(shù))、字符串以及枚舉類型。下面的代碼無法通過編譯:
public const DateTime D = DateTime.MinValue;
改成readonly就可以正常編譯:
public readonly DateTime D = DateTime.MinValue;
可維護(hù)性
readonly以引用方式進(jìn)行工作,某個(gè)常量更新后,所有引用該常量的地方均能得到更新后的值。
const的情況要稍稍復(fù)雜些,特別是跨程序集調(diào)用:
public class ClassA
{
public static readonly int A = 2; //A為運(yùn)行時(shí)常量
public const int B = 3; //B為編譯時(shí)常量
}
public class ClassB
{
public static int C = ClassA.A + ClassA.B; //變量C的值為A、B之和
}
Console.WriteLine(ClassB.C); //輸出"5"
假設(shè)Class1與Class2位于兩個(gè)不同的程序集,現(xiàn)在更改Class1中的常量值:
public class ClassA
{
public static readonly int A = 4; //A為運(yùn)行時(shí)常量
public const int B = 5; //B為編譯時(shí)常量
}
編譯ClassA并部署(注意:這時(shí)并沒有重新編譯ClassB),再次查看變量C的值:
Console.WriteLine(ClassB.C); //輸出"7"
結(jié)果可能有點(diǎn)出乎意料,讓我們來仔細(xì)觀察變量C的賦值表達(dá)式:
public static int C = ClassA.A + ClassA.B;
編譯后與下面的形式等價(jià):
public static int C = ClassA.A + 3;
因此不管常量B的值如何變,對(duì)最終結(jié)果都不會(huì)產(chǎn)生影響。雖說重新編譯ClassB即可解決這個(gè)問題,但至少讓我們看到了const可能帶來的維護(hù)問題。
性能比較
const直接以字面量形式參與運(yùn)算,性能要略高于readonly,但對(duì)于一般應(yīng)用而言,這種性能上的差別可以說是微乎其微。
適用場景
在下面兩種情況下:
a.取值永久不變(比如圓周率、一天包含的小時(shí)數(shù)、地球的半徑等)
b.對(duì)程序性能要求非??量?br>
可以使用const常量,除此之外的其他情況都應(yīng)該優(yōu)先采用readonly常量。
C#中的static 和Java中的static
簡單,兩者用法完全是一致的。從兩方面討論:
- 變量是屬于類的,不是實(shí)例級(jí)別的。只能通過類名調(diào)用,不能通過實(shí)例調(diào)用。
- 如果在定義時(shí)就賦值了,那么在類初始化的時(shí)候,最先完成所有靜態(tài)變量的賦值。但是要注意,所有靜態(tài)變量的初始化順序是無法確定的。
C# 中的const 和Java中的finnal
很長一段時(shí)間我一直認(rèn)為兩者是相同的作用,無非是變量初始化后不能更改,即只能在定義時(shí)或者構(gòu)造函數(shù)中賦值。然而這僅僅只是片面的,下面將為大家詳細(xì)分析:
1.修飾變量
準(zhǔn)確的說C#中的const 等價(jià)于 Java中的static final,也就是說,Java中final不具有static的功能。而C#中的const具有static的功能。因此在C#中 public static const string 等將于 public const string。
2.修飾類和方法
此時(shí)Java中的final類似C#中的sealed,就是說,final修飾的類不能被繼承,final修飾的方法不能被覆蓋。
而C#中的const不能修飾類和方法。
問題:
1. 私有靜態(tài)成員的作用(private static 變量)
字面表示私有的,類外不能使用;靜態(tài)的,全局變量??瓷先ズ苊埽植荒鼙活愅馐褂?,要全局的有什么用。問得好,類中全局也是很有意義的,例如 private static int a = 5,那么就可以保證變量a在類的初始化過程中將被優(yōu)先初始化(在構(gòu)造函數(shù)執(zhí)行之前)。這樣如果對(duì)象A的初始化需要對(duì)象B的實(shí)例,那么就可以用這種申明,以保證在類A在構(gòu)造函數(shù)中能夠使用類B的實(shí)例。同時(shí)private又能夠保證類B的實(shí)例只能在類A中使用,起到很好的密封作用。
2. 私有最終成員作用(private final 變量)
在類構(gòu)造函數(shù)完成前必須對(duì)該成員完成初始化,一旦定義不許更改;該成員只能在本類中使用。實(shí)例,子類中都不能使用。
private static final修飾的成員在申明的時(shí)就被賦值,保證在構(gòu)造函數(shù)中可以被使用,一個(gè)被private static final修飾的成員通常表示其他組件的一個(gè)實(shí)例,且變量是類中的全局變量。
private final 修飾的成員在構(gòu)造中被賦值,表示它是該類全局的私有成員變量,且該類的構(gòu)造需要傳入他們的初始值,才能完成類的初始化。
C# const和static readonly區(qū)別
const:用const修飾符聲明的成員叫常量,是在編譯期初始化并嵌入到客戶端程序
static readonly: 用static readonly修飾符聲明的成員依然是變量,只不過具有和常量類似的使用方法:通過類進(jìn)行訪問、初始化后不可以修改。但與常量不同的是這種變量是在運(yùn)行期初始化。