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

C#基礎:基于const與readonly的深入研究

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

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

復制代碼 代碼如下:

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

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

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]";
    }
}

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

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

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() +"]";
    }
}

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

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

public static readonly int A = 2; //A為運行時常量
public const int B = 3; //B為編譯時常量

下面的表達式:
int C = A + B;
經(jīng)過編譯后與下面的形式等價:
int C = A + 3;
可以看到,其中的const常量B被替換成字面量3,而readonly常量A則保持引用方式。
聲明及初始化
readonly常量只能聲明為類字段,支持實例類型或靜態(tài)類型,可以在聲明的同時初始化或者在構(gòu)造函數(shù)中進行初始化,初始化完成后便無法更改。
const常量除了可以聲明為類字段之外,還可以聲明為方法中的局部常量,默認為靜態(tài)類型(無需用static修飾,否則將導致編譯錯誤),但必須在聲明的同時完成初始化。
數(shù)據(jù)類型支持
由于const常量在編譯時將被替換為字面量,使得其取值類型受到了一定限制。const常量只能被賦予數(shù)字(整數(shù)、浮點數(shù))、字符串以及枚舉類型。下面的代碼無法通過編譯:
復制代碼 代碼如下:

public const DateTime D = DateTime.MinValue;

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

public readonly DateTime D = DateTime.MinValue;

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

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

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

public class Class1
{
    public static readonly int A = 4; //A為運行時常量
    public const int B = 5; //B為編譯時常量
}

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

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

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

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

編譯后與下面的形式等價:
復制代碼 代碼如下:

public static int C = Class1.A + 3;

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

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);
    }
}

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

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);
    }
}

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

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

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

                                                                                        const

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

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

            

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


相關文章

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

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

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

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

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

    使用C#中的Flags特性

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

    C#設計模式之Builder生成器模式解決帶老婆配置電腦問題實例

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

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

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

    c#打包文件解壓縮的實例

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

    C#使用對象序列化類庫MessasgePack

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

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

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

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

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

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

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

最新評論