亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 編程 > C# > 正文

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

2020-01-24 03:16:15
字體:
來源:轉載
供稿:網友

•readonly和const都是用來標識常量的[1]。
•const可用于修飾class的field或者一個局部變量(local variable);而readonly僅僅用于修飾class的field。
•const常量的值必定在編譯時就已明確并且恒定的;而readonly常量卻有一點不同,那就是其值可以在運行時編譯,當然,它也必須遵守作為常量的約束,那就是值必須恒定不變。
•const常量必須在聲明的同時對其進行賦值,并且確保該值在編譯時可確定并恒定;而readonly常量則可以根據情況選擇在聲明的同時對其賦予一個編譯時確定并恒定的值,或者將其值的初始化工作交給實例構造函數(instant constructor)完成。如:public readonly string m_Now = DateTime.Now.ToString();,m_Now會隨著運行時實際情況變化而變化。
•const常量屬于類級別(class level)而不是實例對象級別(instant object level),并且它不能跟static結合一起使用,該常量的值將由整個類的所有實例對象共同分享(詳細論述參見后面的Remark區域)。
•readonly常量既可以是類級別也可以是實例對象級別的,這取決于它的聲明以及初始化工作怎么實施。readonly可以與static結合使用,用于指定該常量屬于類級別,并且把初始化工作交由靜態構造函數(static constructor)完成(有關如何把readonly常量聲明為類級別或實例對象級別的論述清參見后面的Remark區域)。
•能被const修飾聲明為常量的類型必須是以下的基元類型(primitive type):sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, float, bool, decimal, string。
•object, 數組(Array)和結構(struct)不能被聲明為const常量。
•一般情況下,引用類型是不能被聲明為const常量的,不過有一個例外:string。該引用類型const常量的值可以有兩種情況,string或null。其實,string雖然是引用類型,但是.NET卻對它特別處理,這種處理叫做字符串恒定性(immutable),使得string的值具有只讀特性。有關字符串恒定性的內容,可以參考《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:
•如果結合數據庫使用,ID field通常都會都會與某個表的主?。╬rimary key)關聯起來,如Orders表的OrderID。
•數據庫的主健通常采用以下三種方式:
•自動遞增值。你可以通過把DataColumn.AutoIncrement設定為true值來激活自動遞增特性。
•唯一名稱。這個是使用自己定義的算法來生成一個唯一序列號。
•GUID(全局唯一標識符)。你可以通過System.Guid結構來生成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。
•另外,如果要表示對象內部的默認值的話,而這類值通常是常量性質的,那么也可以考慮const。更多時候我們對源代碼進行重構時(使用Replace Magic Number with Symbolic Constant),要去除魔數(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的時候,發現ReadonlyInt和InstantReadonlyInt需要指定Constant的實例對象;而ConstInt和StaticReadonlyInt卻要指定Constant class(參見上面代碼)??梢姡胏onst或者static readonly修飾的常量是屬于類級別的;而readonly修飾的,無論是直接通過賦值來初始化或者在實例構造函數里初始化,都屬于實例對象級別。
•一般情況下,如果你需要表達一組相關的編譯時確定常量,你可以考慮使用枚舉類型(enum),而不是把多個const常量直接嵌入到class中作為field,不過這兩種方式沒有絕對的孰優孰劣之分。
復制代碼 代碼如下:

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

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

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

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

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

public const DateTime D = DateTime.MinValue;

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

public readonly DateTime D = DateTime.MinValue;

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

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位于兩個不同的程序集,現在更改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"

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

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

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

public static int C = Class1.A + 3;

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

對于上述代碼,輸出結果是多少?很多人會認為是A is 100,B is 10吧!其實,正確的輸出結果是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);
    }
}

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

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

      
   
static readonly可執行程序的結構 

                                                                                        const

可執行程序的結構

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

            

static readonly修飾的常量A   

 

const修飾的常量A

          

static readonly修飾的常量B

 

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

             

static readonly程序的Main函數

       

const程序的Main函數

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


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
高清欧美性猛交xxxx黑人猛交| 久久影院中文字幕| 欧美成人免费大片| 久久久久久久网站| 神马久久桃色视频| 7777免费精品视频| 热久久美女精品天天吊色| 欧美富婆性猛交| 8090成年在线看片午夜| 精品久久国产精品| 成人中文字幕在线观看| 久久久999精品| 欧美日韩国产色| 国产精品va在线| 成人av色在线观看| 欧美性猛交xxxxx免费看| 精品欧美国产一区二区三区| 亚洲精品www久久久久久广东| 国产精品一区二区久久| www.日韩视频| 国产精品视频一区二区三区四| 国产精品18久久久久久首页狼| 久久久久女教师免费一区| 亚洲黄色av女优在线观看| 国产日韩中文在线| 2021久久精品国产99国产精品| 国产成人一区二区三区| 亚洲视频免费一区| 色综合91久久精品中文字幕| 亚洲一区二区三区毛片| 欧美极品少妇xxxxⅹ免费视频| 午夜精品一区二区三区在线播放| 蜜臀久久99精品久久久久久宅男| 国产精品久久久久久久久久久久久久| 中文字幕亚洲无线码a| 成人亚洲欧美一区二区三区| 成人免费网站在线看| 国内精品模特av私拍在线观看| 成人福利网站在线观看| 欧美老女人bb| 久久久亚洲精选| 精品国偷自产在线视频99| 亚洲人成电影网站| 国产精品亚洲第一区| 777午夜精品福利在线观看| 亚洲欧美视频在线| 搡老女人一区二区三区视频tv| 亚洲品质视频自拍网| 2018国产精品视频| 欧美电影免费观看电视剧大全| 亚洲精品aⅴ中文字幕乱码| 久久久综合av| 日本人成精品视频在线| 国产欧美最新羞羞视频在线观看| 国产成人综合精品| 欧美区二区三区| 国产精品亚洲视频在线观看| 日韩欧美国产骚| 日韩成人av网址| 午夜精品国产精品大乳美女| 亚洲第一色中文字幕| 日韩美女免费视频| 欧洲午夜精品久久久| 伊人一区二区三区久久精品| 成人免费视频网| 国产精品自拍小视频| 国产69精品99久久久久久宅男| 欧美日韩国产精品一区| 欧美天天综合色影久久精品| 一本色道久久综合亚洲精品小说| wwwwwwww亚洲| 日韩成人av在线播放| 国产成人精品久久二区二区| 国产精品嫩草影院一区二区| 亚洲国产成人久久综合| 97视频在线观看免费高清完整版在线观看| 欧美中文字幕视频在线观看| 色无极影院亚洲| 欧美在线视频网| 国产综合色香蕉精品| 国产成人在线一区二区| 国产精品91一区| 亚洲影院色无极综合| 日韩av免费观影| 91亚洲精品在线观看| 日韩视频精品在线| 爽爽爽爽爽爽爽成人免费观看| 欧美精品在线看| 91精品久久久久久久久中文字幕| 亚洲激情自拍图| 久久精品国产v日韩v亚洲| 亚洲国产小视频在线观看| 久久成人一区二区| 精品激情国产视频| 日本欧美黄网站| 亚洲第一区在线| 久热精品视频在线免费观看| 97不卡在线视频| 色婷婷综合久久久久| 中文字幕精品在线视频| 国产主播喷水一区二区| 黑人精品xxx一区一二区| 亚洲一区二区三区乱码aⅴ| 日韩欧美在线观看| 亚洲男人天堂2023| 欧美大片欧美激情性色a∨久久| 久久久噜噜噜久久久| 日本最新高清不卡中文字幕| 91av网站在线播放| 538国产精品一区二区在线| 在线午夜精品自拍| 日韩美女视频免费看| 久久久人成影片一区二区三区观看| 国产精品青青在线观看爽香蕉| 亚洲一区二区黄| 亚洲一区二区三区成人在线视频精品| 丝袜美腿精品国产二区| 91视频88av| 456亚洲影院| 蜜月aⅴ免费一区二区三区| 亚洲天堂免费在线| 日韩av电影中文字幕| 国产精品久久视频| 国产综合在线看| 久久精品99无色码中文字幕| 九九热视频这里只有精品| 欧美在线性爱视频| 怡红院精品视频| 久久久久在线观看| 日韩在线视频线视频免费网站| 国产精品视频男人的天堂| 在线播放国产精品| 日韩电影中文字幕在线观看| 九九九热精品免费视频观看网站| 亚洲91精品在线观看| 国产精品激情av电影在线观看| 综合国产在线视频| 最新国产精品拍自在线播放| 欧美wwwxxxx| 97色伦亚洲国产| 91色视频在线观看| 欧美黄色片在线观看| 一区二区福利视频| 日本不卡视频在线播放| 欧美成人自拍视频| 国产成人福利网站| 国产精品入口免费视| 日韩av在线影视| 国产一区二区三区视频免费| 欧美激情手机在线视频| 亚洲免费成人av电影| 在线观看国产成人av片| 欧美多人爱爱视频网站| 久久久黄色av| 中文字幕av一区| 亚洲一级黄色av| 韩国日本不卡在线| 欧美性xxxx极品hd欧美风情| 色综合久久中文字幕综合网小说| 欧美一区亚洲一区| 中文字幕精品久久| 国产欧美一区二区白浆黑人| 欧美视频免费在线观看| 亚洲电影在线观看|