欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C#基礎(chǔ):基于const與readonly的深入研究

 更新時(shí)間:2013年05月22日 17:22:07   作者:  
本篇文章是對(duì)c#中const與readonly進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下

•readonly和const都是用來標(biāo)識(shí)常量的[1]。
•const可用于修飾class的field或者一個(gè)局部變量(local variable);而readonly僅僅用于修飾class的field。
•const常量的值必定在編譯時(shí)就已明確并且恒定的;而readonly常量卻有一點(diǎn)不同,那就是其值可以在運(yùn)行時(shí)編譯,當(dāng)然,它也必須遵守作為常量的約束,那就是值必須恒定不變。
•const常量必須在聲明的同時(shí)對(duì)其進(jìn)行賦值,并且確保該值在編譯時(shí)可確定并恒定;而readonly常量則可以根據(jù)情況選擇在聲明的同時(shí)對(duì)其賦予一個(gè)編譯時(shí)確定并恒定的值,或者將其值的初始化工作交給實(shí)例構(gòu)造函數(shù)(instant constructor)完成。如:public readonly string m_Now = DateTime.Now.ToString();,m_Now會(huì)隨著運(yùn)行時(shí)實(shí)際情況變化而變化。
•const常量屬于類級(jí)別(class level)而不是實(shí)例對(duì)象級(jí)別(instant object level),并且它不能跟static結(jié)合一起使用,該常量的值將由整個(gè)類的所有實(shí)例對(duì)象共同分享(詳細(xì)論述參見后面的Remark區(qū)域)。
•readonly常量既可以是類級(jí)別也可以是實(shí)例對(duì)象級(jí)別的,這取決于它的聲明以及初始化工作怎么實(shí)施。readonly可以與static結(jié)合使用,用于指定該常量屬于類級(jí)別,并且把初始化工作交由靜態(tài)構(gòu)造函數(shù)(static constructor)完成(有關(guān)如何把readonly常量聲明為類級(jí)別或?qū)嵗龑?duì)象級(jí)別的論述清參見后面的Remark區(qū)域)。
•能被const修飾聲明為常量的類型必須是以下的基元類型(primitive type):sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, float, bool, decimal, string。
•object, 數(shù)組(Array)和結(jié)構(gòu)(struct)不能被聲明為const常量。
•一般情況下,引用類型是不能被聲明為const常量的,不過有一個(gè)例外:string。該引用類型const常量的值可以有兩種情況,string或null。其實(shí),string雖然是引用類型,但是.NET卻對(duì)它特別處理,這種處理叫做字符串恒定性(immutable),使得string的值具有只讀特性。有關(guān)字符串恒定性的內(nèi)容,可以參考《Microsoft .NET框架程序設(shè)計(jì)(修訂版)》。
Examples:

復(fù)制代碼 代碼如下:

Code
using System;
publicclass Order
{
    public Order()
    {
        Guid guid = Guid.NewGuid();
        ID = guid.ToString("D");
    }
    // 對(duì)于每一份訂單,其訂單序號(hào)都是實(shí)時(shí)確定的常量。
 style="color: #008000;">
publicreadonlystring ID;
    publicoverridestring ToString()
    {
        return"Order ID: "+ ID;
    }
}

Explaintion:
•如果結(jié)合數(shù)據(jù)庫使用,ID field通常都會(huì)都會(huì)與某個(gè)表的主?。╬rimary key)關(guān)聯(lián)起來,如Orders表的OrderID。
•數(shù)據(jù)庫的主健通常采用以下三種方式:
•自動(dòng)遞增值。你可以通過把DataColumn.AutoIncrement設(shè)定為true值來激活自動(dòng)遞增特性。
•唯一名稱。這個(gè)是使用自己定義的算法來生成一個(gè)唯一序列號(hào)。
•GUID(全局唯一標(biāo)識(shí)符)。你可以通過System.Guid結(jié)構(gòu)來生成GUID,如上例。
復(fù)制代碼 代碼如下:

Code
using System;
class Customer
{
    public Customer(string name, int kind)
    {
        m_Name = name;
        m_Kind = kind;
    }
    publicconstint NORMAL =0;
    publicconstint VIP =1;
    publicconstint SUPER_VIP =2;
    privatestring m_Name;
    publicstring Name
    {
        get { return m_Name; }
    }
    privatereadonlyint m_Kind;
    publicint Kind
    {
        get { return m_Kind; }
    }
    publicoverridestring ToString()
    {
        if(m_Kind == SUPER_VIP)
            return"Name: "+ m_Name +"[SuperVip]";
        elseif(m_Kind == VIP)
            return"Name: "+ m_Name +"[Vip]";
        else
            return"Name: "+ m_Name +"[Normal]";
    }
}

•一般情況下,如果你需要聲明的常量是普遍公認(rèn)的并作為單個(gè)使用,例如圓周率,黃金分割比例等。你可以考慮使用const常量,如:public const double PI = 3.1415926;。如果你需要聲明常量,不過這個(gè)常量會(huì)隨著實(shí)際的運(yùn)行情況而決定,那么,readonly常量將會(huì)是一個(gè)不錯(cuò)的選擇,例如上面第一個(gè)例子的訂單號(hào)Order.ID。
•另外,如果要表示對(duì)象內(nèi)部的默認(rèn)值的話,而這類值通常是常量性質(zhì)的,那么也可以考慮const。更多時(shí)候我們對(duì)源代碼進(jìn)行重構(gòu)時(shí)(使用Replace Magic Number with Symbolic Constant),要去除魔數(shù)(Magic Number)的影響都會(huì)借助于const的這種特性。
•對(duì)于readonly和const所修飾的變量究竟是屬于類級(jí)別的還是實(shí)例對(duì)象級(jí)別的問題,我們先看看如下代碼:
復(fù)制代碼 代碼如下:

Code
using System;
namespace ConstantLab
{
    class Program
    {
        staticvoid Main(string[] args)
        {
            Constant c =new Constant(3);
            Console.WriteLine("ConstInt = "+ Constant.ConstInt.ToString());
            Console.WriteLine("ReadonlyInt = "+ c.ReadonlyInt.ToString());
            Console.WriteLine("InstantReadonlyInt = "+ c.InstantReadonlyInt.ToString());
            Console.WriteLine("StaticReadonlyInt = "+ Constant.StaticReadonlyInt.ToString());
            Console.WriteLine("Press any key to continue");
            Console.ReadLine();
        }
    }
    class Constant
    {
        public Constant(int instantReadonlyInt)
        {
            InstantReadonlyInt = instantReadonlyInt;
        }
        publicconstint ConstInt =0;
        publicreadonlyint ReadonlyInt =1;
        publicreadonlyint InstantReadonlyInt;
        publicstaticreadonlyint StaticReadonlyInt =4;
    }
}

•使用Visual C#在Main()里面使用IntelliSence插入Constant的相關(guān)field的時(shí)候,發(fā)現(xiàn)ReadonlyInt和InstantReadonlyInt需要指定Constant的實(shí)例對(duì)象;而ConstInt和StaticReadonlyInt卻要指定Constant class(參見上面代碼)??梢姡胏onst或者static readonly修飾的常量是屬于類級(jí)別的;而readonly修飾的,無論是直接通過賦值來初始化或者在實(shí)例構(gòu)造函數(shù)里初始化,都屬于實(shí)例對(duì)象級(jí)別。
•一般情況下,如果你需要表達(dá)一組相關(guān)的編譯時(shí)確定常量,你可以考慮使用枚舉類型(enum),而不是把多個(gè)const常量直接嵌入到class中作為field,不過這兩種方式?jīng)]有絕對(duì)的孰優(yōu)孰劣之分。
復(fù)制代碼 代碼如下:

Code
using System;
enum CustomerKind
{
    SuperVip,
    Vip,
    Normal
}
class Customer
{
    public Customer(string name, CustomerKind kind)
    {
        m_Name = name;
        m_Kind = kind;
    }
    privatestring m_Name;
    publicstring Name
    {
        get { return m_Name; }
    }
    private CustomerKind m_Kind;
    public CustomerKind Kind
    {
        get { return m_Kind; }
    }
    publicoverridestring ToString()
    {
        return"Name: "+ m_Name +"["+ m_Kind.ToString() +"]";
    }
}

然而,當(dāng)這種結(jié)合使用枚舉和條件判斷的代碼阻礙了你進(jìn)行更靈活的擴(kuò)展,并有可能導(dǎo)致日后的維護(hù)成本增加,你可以代之以多態(tài),使用Replace Conditional with Polymorphism來對(duì)代碼進(jìn)行重構(gòu).
Comments:
•readonly field準(zhǔn)確來說應(yīng)該翻譯成為“只讀域”,這里是為了統(tǒng)一翻譯用語才將它和const兩者所修飾的量都說成“常量”,希望沒有引起誤會(huì)。

工作原理
    readonly為運(yùn)行時(shí)常量,程序運(yùn)行時(shí)進(jìn)行賦值,賦值完成后便無法更改,因此也有人稱其為只讀變量。
    const為編譯時(shí)常量,程序編譯時(shí)將對(duì)常量值進(jìn)行解析,并將所有常量引用替換為相應(yīng)值。
    下面聲明兩個(gè)常量:
復(fù)制代碼 代碼如下:

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ù))、字符串以及枚舉類型。下面的代碼無法通過編譯:
復(fù)制代碼 代碼如下:

public const DateTime D = DateTime.MinValue;

改成readonly就可以正常編譯:
復(fù)制代碼 代碼如下:

public readonly DateTime D = DateTime.MinValue;

可維護(hù)性
readonly以引用方式進(jìn)行工作,某個(gè)常量更新后,所有引用該常量的地方均能得到更新后的值。
const的情況要稍稍復(fù)雜些,特別是跨程序集調(diào)用:
復(fù)制代碼 代碼如下:

public class Class1
{
    public static readonly int A = 2; //A為運(yùn)行時(shí)常量
    public const int B = 3; //B為編譯時(shí)常量
}
public class Class2
{
    public static int C = Class1.A + Class1.B; //變量C的值為A、B之和
}
Console.WriteLine(Class2.C); //輸出"5"

假設(shè)Class1與Class2位于兩個(gè)不同的程序集,現(xiàn)在更改Class1中的常量值:
復(fù)制代碼 代碼如下:

public class Class1
{
    public static readonly int A = 4; //A為運(yùn)行時(shí)常量
    public const int B = 5; //B為編譯時(shí)常量
}

編譯Class1并部署(注意:這時(shí)并沒有重新編譯Class2),再次查看變量C的值:
復(fù)制代碼 代碼如下:

Console.WriteLine(Class2.C); //輸出"7"

結(jié)果可能有點(diǎn)出乎意料,讓我們來仔細(xì)觀察變量C的賦值表達(dá)式:
復(fù)制代碼 代碼如下:

public static int C = Class1.A + Class1.B;

編譯后與下面的形式等價(jià):
復(fù)制代碼 代碼如下:

public static int C = Class1.A + 3;

因此不管常量B的值如何變,對(duì)最終結(jié)果都不會(huì)產(chǎn)生影響。雖說重新編譯Class2即可解決這個(gè)問題,但至少讓我們看到了const可能帶來的維護(hù)問題。
性能比較
const直接以字面量形式參與運(yùn)算,性能要略高于readonly,但對(duì)于一般應(yīng)用而言,這種性能上的差別可以說是微乎其微。
適用場(chǎng)景
在下面兩種情況下:
a.取值永久不變(比如圓周率、一天包含的小時(shí)數(shù)、地球的半徑等)
b.對(duì)程序性能要求非常苛刻
可以使用const常量,除此之外的其他情況都應(yīng)該優(yōu)先采用readonly常量。
盡管你寫了很多年的C#的代碼,但是可能當(dāng)別人問到你const與readonly的區(qū)別時(shí)候,還是會(huì)小小的愣一會(huì)吧~
筆者也是在看歐立奇版的《.Net 程序員面試寶典》的時(shí)候,才發(fā)現(xiàn)自己長(zhǎng)久以來竟然在弄不清出兩者的情況下,混用了這么長(zhǎng)的時(shí)間。的確,const與readonly 很像,都是將變量聲明為只讀,且在變量初始化后就不可改寫。那么,const與readonly 這兩個(gè)修飾符到底區(qū)別在什么地方呢?其實(shí),這個(gè)牽扯出C#語言中兩種不同的常量類型:靜態(tài)常量(compile-time constants)和動(dòng)態(tài)常量(runtime constants)。這兩者具有不同的特性,錯(cuò)誤的使用不僅會(huì)損失效率,而且還會(huì)造成錯(cuò)誤。
首先先解釋下什么是靜態(tài)常量以及什么是動(dòng)態(tài)常量。靜態(tài)常量是指編譯器在編譯時(shí)候會(huì)對(duì)常量進(jìn)行解析,并將常量的值替換成初始化的那個(gè)值。而動(dòng)態(tài)常量的值則是在運(yùn)行的那一刻才獲得的,編譯器編譯期間將其標(biāo)示為只讀常量,而不用常量的值代替,這樣動(dòng)態(tài)常量不必在聲明的時(shí)候就初始化,而可以延遲到構(gòu)造函數(shù)中初始化。
當(dāng)你大致了解上面的兩個(gè)概念的時(shí)候,那么就可以來說明const與readonly了。const修飾的常量是上述中的第一種,即靜態(tài)常量;而readonly則是第二種,即動(dòng)態(tài)常量。那么區(qū)別可以通過靜態(tài)常量與動(dòng)態(tài)常量的特性來說明:
1)const修飾的常量在聲明的時(shí)候必須初始化;readonly修飾的常量則可以延遲到構(gòu)造函數(shù)初始化
2)const修飾的常量在編譯期間就被解析,即常量值被替換成初始化的值;readonly修飾的常量則延遲到運(yùn)行的時(shí)候
此外const常量既可以聲明在類中也可以在函數(shù)體內(nèi),但是static readonly常量只能聲明在類中。
可能通過上述純概念性的講解,對(duì)有些初學(xué)者有些暈乎。下面就一些例子來說明下:
復(fù)制代碼 代碼如下:

using System;
class P
{
    static readonly int A=B*10;
    static readonly int B=10;  
    public static void Main(string[] args)
    {
        Console.WriteLine("A is {0},B is {1} ",A,B);
    }
}

對(duì)于上述代碼,輸出結(jié)果是多少?很多人會(huì)認(rèn)為是A is 100,B is 10吧!其實(shí),正確的輸出結(jié)果是A is 0,B is 10。好吧,如果改成下面的話:
復(fù)制代碼 代碼如下:

using System;
class P
{
    const int A=B*10;
    const int B=10;  
    public static void Main(string[] args)
    {
        Console.WriteLine("A is {0},B is {1} ",A,B);
    }
}

對(duì)于上述代碼,輸出結(jié)果又是多少呢?難道是A is 0,B is 10?其實(shí)又錯(cuò)了,這次正確的輸出結(jié)果是A is 100,B is 10。
那么為什么是這樣的呢?其實(shí)在上面說了,const是靜態(tài)常量,所以在編譯的時(shí)候就將A與B的值確定下來了(即B變量時(shí)10,而A=B*10=10*10=100),那么Main函數(shù)中的輸出當(dāng)然是A is 100,B is 10啦。而static readonly則是動(dòng)態(tài)常量,變量的值在編譯期間不予以解析,所以開始都是默認(rèn)值,像A與B都是int類型,故都是0。而在程序執(zhí)行到A=B*10;所以A=0*10=0,程序接著執(zhí)行到B=10這句時(shí)候,才會(huì)真正的B的初值10賦給B。如果,你還是不大清楚的話,我們可以借助于微軟提供的ILDASM工具,只需在Vs 2008 Command下輸入ILDASM就可以打開,如下所示:
              

分別打開上述兩個(gè)代碼編譯后產(chǎn)生的可執(zhí)行文件,如下圖所示:

      
   
static readonly可執(zhí)行程序的結(jié)構(gòu) 

                                                                                        const

可執(zhí)行程序的結(jié)構(gòu)

在上述兩張圖中都可以看到A與B常量,分別雙擊節(jié)點(diǎn)可以看出其中的差異:

            

static readonly修飾的常量A   

 

const修飾的常量A

          

static readonly修飾的常量B

 

const修飾的常量B
從上圖中可以看出,const修飾的常量在編譯期間便已將A,B的字面值算出來了,而static readonly修飾的常量則未解析,所以在Main函數(shù)中有以下的區(qū)別:

             

static readonly程序的Main函數(shù)

       

const程序的Main函數(shù)

從Main函數(shù)中我們可以看出,const的那個(gè)程序的輸出直接是100與10,而readonly在輸出的時(shí)候確實(shí)P::A與P::B,即將A與B常量的值延遲到運(yùn)行的時(shí)候才去確定,故輸出是0與10。
那么對(duì)于靜態(tài)常量以及動(dòng)態(tài)常量還有什么特性呢?其實(shí),靜態(tài)常量只能被聲明為簡(jiǎn)單的數(shù)據(jù)類型(int以及浮點(diǎn)型)、枚舉、布爾或者字符串型,而動(dòng)態(tài)常量則除了這些類型,還可以修飾一些對(duì)象類型。如DateTime類型,如下:
//錯(cuò)誤
const DateTime time=new DateTime();
//正確
static readonly DateTime time=new DateTime();
上述錯(cuò)誤在于不能使用new關(guān)鍵字初始化一個(gè)靜態(tài)常量,即便是一個(gè)值類型,因?yàn)閚ew將會(huì)導(dǎo)致到運(yùn)行時(shí)才能確定值,與靜態(tài)變量編譯時(shí)就確定字面值有悖。    
歐書上最后給出了對(duì)靜態(tài)常量與動(dòng)態(tài)常量之間的比較,如下表所示:   
     


相關(guān)文章

  • C#代替go采用的CSP并發(fā)模型實(shí)現(xiàn)

    C#代替go采用的CSP并發(fā)模型實(shí)現(xiàn)

    這篇文章主要為大家介紹了C#代替go采用的CSP并發(fā)模型的輕松實(shí)現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪
    2022-04-04
  • c# 多線程處理多個(gè)數(shù)據(jù)的方法

    c# 多線程處理多個(gè)數(shù)據(jù)的方法

    這篇文章主要介紹了c# 多線程處理多個(gè)數(shù)據(jù)的方法,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下
    2021-03-03
  • 使用C#中的Flags特性

    使用C#中的Flags特性

    這篇文章介紹了使用C#中的Flags特性,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • C#設(shè)計(jì)模式之Builder生成器模式解決帶老婆配置電腦問題實(shí)例

    C#設(shè)計(jì)模式之Builder生成器模式解決帶老婆配置電腦問題實(shí)例

    這篇文章主要介紹了C#設(shè)計(jì)模式之Builder生成器模式解決帶老婆配置電腦問題,簡(jiǎn)單介紹了生成器模式的概念、功能并結(jié)合具體實(shí)例形式分析了C#生成器模式解決配電腦問題的步驟與相關(guān)操作技巧,需要的朋友可以參考下
    2017-09-09
  • C#組合函數(shù)的使用詳解

    C#組合函數(shù)的使用詳解

    本篇文章是對(duì)C#中的組合函數(shù)的使用進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • c#打包文件解壓縮的實(shí)例

    c#打包文件解壓縮的實(shí)例

    下面小編就為大家分享一篇c#打包文件解壓縮的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2017-11-11
  • C#使用對(duì)象序列化類庫MessasgePack

    C#使用對(duì)象序列化類庫MessasgePack

    這篇文章介紹了C#使用對(duì)象序列化類庫MessasgePack的方法,文中通過示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • c# 連接access數(shù)據(jù)庫config配置

    c# 連接access數(shù)據(jù)庫config配置

    c# 連接access數(shù)據(jù)庫config配置,需要的朋友可以參考一下
    2013-02-02
  • C# 獲取當(dāng)前年份的周期及周期所在日期范圍(推薦)

    C# 獲取當(dāng)前年份的周期及周期所在日期范圍(推薦)

    這篇文章主要介紹了C# 獲取當(dāng)前年份的周期,周期所在日期范圍 ,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2018-05-05
  • C#程序員應(yīng)該養(yǎng)成的程序性能優(yōu)化寫法

    C#程序員應(yīng)該養(yǎng)成的程序性能優(yōu)化寫法

    工作和生活中經(jīng)??梢钥吹揭恍┏绦蛟?寫代碼的時(shí)候只關(guān)注代碼的邏輯性,而不考慮運(yùn)行效率,其實(shí)這對(duì)大多數(shù)程序猿來說都是沒有問題的,不過作為一只有理想的CodeMonkey,我還是希望給大家分享一些性能優(yōu)化心得
    2017-08-08

最新評(píng)論