close

***所以說啊常數是什麼***

依據 C#程式設計手冊,常數(Constants)的定義為 : Constants are immutable values which are known at compile time and do not change for the life of the program.

常數是在編譯時期就已經知道的,一個不會改變的數值,並且在程式的生命週期中都不允許修改。(不允許修改很重要,請抄寫50次)

且常數可以為特別的數值提供有意義的名稱,而非Magic Number ,影響程式的可讀性

在學程式時,常被用來舉例的就是 圓 周 率。每個人國中小絕對都學過,3.1415926..........

大家都知道,圓周率是不會變的,但如果被不知情的( ? )其他工程師改了,那真的是欲哭無淚,覺得不知道是誰在整自己~

這種類型(恆不變的數值),我們就會宣告為常數~

一開始寫Java的時候,常數很單純,final 就對了 ( 後來聽說還有另一個 )

開始寫C#之後,卻有點懷疑人參~ 

( 這次寫的內容有點長,急著找答案的人歡迎看懶人包 : 點我 )

 


 

***C#的const及readonly***

在Google搜尋「C# 常數」,其實十有八九出現的還是const

但夜路走多了,會發現其實還有另一個牽扯不清的關鍵字 : readonly

在Google搜尋的話,兩者因其不同的特性,可以找到很多不同的稱呼

const       :  Constants常數,稱之為「靜態常數」,或稱「編譯階段常數」

      在編譯階段即解析 常數運算式 , 並將其值指派給所宣告的常數。

readonly  : 字面意思「唯讀」,又稱「動態常數」,或稱「執行階段常數」

      在執行時才獲得實際的數值。

到底實際上有什麼差別? 讓我們看下去~

 


 

***先來談談const ( Constants ) ***

**const的宣告**

使用const關鍵字,只有C#內建型別,如數值、布林、字串或是Null參考可以被指定為常數。

Null參考是指說,如果const的類型是參考型別,那只能指定其為Null

image

image

關於為什麼 const 在參考型別只能指派為 null , 這點有不少討論文章。主要是因為對 const 來說,其等號右方只能指定為常數。

至於到底有沒有作用,則是見仁見智,有些說法覺得指派 const 為 null ,也可以是增加可讀性的一種。

image

而在生命週期部分,const可用來宣告 常數欄位 或 區域常數 ,且在宣告時就必須指派其數值,否則會出現錯誤:「需要為const欄位提供值」

且如前面所提,因const為靜態常數 無法被指派 非常數運算式,常數運算式是在編譯時期即可評估。

上面的說法感覺很難理解 XD 可以參考下圖 : 無法指派Math.Sin的數值給const,因其是在執行階段運算,故出現錯誤。

image

如果在編譯階段,試圖修改const的值,會出現錯誤:「指派的左側必須是變數、屬性或索引子。」

**命名規則**

在開發的潛規則中,還是會比較常聽到建議 const常數 在命名時 使用全大寫Upper Case,並在每個單字間以 _ ( underscore character ) 分隔,與一般變數、屬性作區別。

不過這還是以團隊開發規範為主,畢竟每個人見解還是不太相同。

**存取修飾詞**

可標記 存取修飾詞(Access modifiers, 如 : public private internal... aso),來定義如何存取常數。

**Static靜態化**

一個 const 常數成員,即被編輯器視為static,其數值在所有實體都相同。

因此 const 不需要標為 static,會出現錯誤:「常數不可標記為static」。

常數的存取方式與 static 欄位相同 : 若不是在定義常數的類別中欲呼叫該常數,則使用類別名稱做為前置、句號(a period)及常數名稱的格式來存取。

image

**特色**

事實上,編譯器在C#中遇到 constant 識別碼時(例如 : January),會在轉為中繼語言(Intermediate Language, IL)時替代為字面數值。

故在執行階段並沒有變數位址關連到常數。

也因const常數沒有占用記憶體,效能較快,但無法以址傳遞,並且無法作為表示式(Expression)的左值 ( 無法放在 指派運算子= 的 左方 )。

也因為 const 在中繼語言會轉為字面數值,如果有參考到定義在其他程式碼 ( 如 : dll ) 的常數,當該 dll 有修改常數的數值,你的程式仍會持有(Hold)原本的字面常數,直到妳重新編譯。

網路上大部分不建議使用 const , 也是因為此點。

image

(請原諒不專業寫手的圖畫得很爛www)

 


 

***那麼readonly呢?***

**readonly的宣告**

相較於const, readonly的規則少很多~

基本型別以至於類別、結構或陣列等等,都可以使用 readonly 修飾詞。同樣的,readonly可指派 非常數運算式。

image

無法宣告為區域變數在方法中使用,會顯示 : 「修飾元'readonly'對此項目無效」

可宣告為 readonly 的型別種類繁多、族繁不及備載。相對的就不是那麼單純,藏著一點小小的秘密。

對於 readonly基本型別 和 readonly參考型別,是有一個很大的差異的 : 「參考型別是記錄其存放數值的記憶體位置,如果一個參考型別欄位是readonly,則其必定參考同一物件。

此物件不可重新指派,readonly修飾詞會預防其被不同的物件取代。然後,readonly修飾詞不會防止物件中的資料欄位被修改。

看起來很長很饒舌,其實就一句話 : 宣告為 readonly 的物件無法重新指派記憶體位置,但該類別的屬性及欄位,不會受物件的 readonly 影響,仍可以修改。

image

另外相較於 const 而言比較特別的是,readonly 欄位可在 宣告 或 建構函式 中賦值。 這是什麼意思呢?

就是說,除了在一開始宣告時指派值之外,我們也可以選擇保留到 建構式 Constructor 中 再指派值。(不知道建構式是什麼的快去查~~~)

因此,readonly 欄位可能會因使用的建構函式而有不同的值( 不像 const 須在一開始宣告時即指定 ),並且之後無法被修改。

image

By the way,在 Constructor 中 可以不受限的重新指派 readonly 欄位。(下面再加一行 CONST_CODE = 5 也沒有問題)

在離開建構子之後就無法再被指派。如果編譯時,在一般函式中修改readonly欄位數值,會出現錯誤:「無法予與指派值的唯讀欄位」

**關於CA2104**

其實這只是在Microsoft Docs中看到的一個無聊小概念,而且也已經過時了。沒有興趣的朋友可以直接跳到下一小段 : 傳送門

在 Microsoft Docs 中的 readonly 說明提到一個Warning CA2104 : 「An externally visible type that contains an externally visible read-only field that is a mutable reference type may be a security vulnerability and may trigger warning CA2104 : "Do not declare read only mutable reference types."

好,看得一頭霧水。那我們看看中文版本的Microsoft Docs : 「外部可見的類型(包含可變動參考型別的外部可見唯讀欄位)可能是安全性弱點,可能會觸發警告 CA2104 :「不要宣告唯讀的可變動參考型別」。

嗯,好。阿鬼你還是說英文吧 ( ? ) XDDD 

總之多翻了幾篇後,可以發現 : 「此警告規則 CA2104 已過時,在VS2017後的版本含2019都已移除。

但基於好奇還是多看了一下,找到一段範例碼如下

image

StringBuild 為可變動參考類型,其為外部可見唯讀欄位(public readonly)。而 string 是 不可變參考型別,即具現化後其值永遠不會變更,

作為 readonly 的 StringBuilder 不但可以重新指派,還可以修改其值。

真是讓我受了大大的驚嚇QQ

好像目前透過某種設定,還是可以出現警告 (畢竟太嚇人) ,不過暫時就先至此,沒深入查看下去了~

(沒事還是別這樣寫嚇人XD 知道一下就好)

**Static靜態化**

在其他文章,常會看到 readonly 與 static 搭配使用。

是的沒錯,readonly 並非預設 static,如欲使用請自行加上。

**特色**

前面有提到,const 沒有耗用記憶體,因此有較佳的效能。既然如此,有什麼理由要用 readonly ?

readonly 因為要保存常數,所以有耗用記憶體,但也因此有較好的彈性、使用上較靈活。

靈活? 怎麼樣的靈活? 他可以旋轉360度嗎? ( 大誤

記得前面講的,當引用來自其他的程式庫(dll) 的 const 常數時,因其在 中繼語言 被轉為 字面數值,使引用方的 dll 也需要重新建置,才能取得更改後的數值。

但如果是 readonly 常數,則引用方的 dll 就不需要重新建置。你問說為什麼? 因為 readonly 所記憶的是路徑,而不是字面數值啊~!

 


 

***剛好看到的某個範例***

另外在數篇文章海中,有篇還真的是長了見識,也或是我太孤陋寡聞。 XDD

有寫過程式的人應該都知道,通常我們必須先宣告一個變數,之後才能使用,否則編譯階段會出現錯誤。

image

之前剛接觸 JavaScript 時,就被 Hoisting 開啟了新的三觀,卻沒想到在C#也可以體驗到 XD

其實在IDE訊息中已經寫得很清楚,中文是 : 「欄位初始設定式無法參考非靜態欄位、方法或屬性」,所以當你加上了static ...

image

不但可以編譯通過, A 還出乎意料的是0~ 但細想想的話,就會覺得好個不意外

只是在文章中的例子是加上 static readonly, 讓我們來看看結果

image

跟 沒有加上 readonly 時是一樣的結果

在一些介紹 static readonly 的範例中看到 , 但實際上這個結果跟 readonly 並沒有關係,而是來自於 static 的概念

特此說明~有錯請糾~

 


 

***補充 : 看到某個常數的寫法***

Microsoft Dosc : How to define constants in C# 中提到,可以建立一個 名稱為 Contants 的靜態類別,再把常數都放進去

這樣需要以 類別名稱限定詞 呼叫時,可以幫助你跟你的小夥伴們都知道,這類別中的數值都是常數~

 


 

***最後的最後***

這篇不知不覺參考資料越看越多、文章越寫越久,大概前前後後搞了一個月有吧。

明明是很簡單的概念,卻藏了很多的細節~

很多文章最後都會建議使用 readonly 而非 const。因為 readonly 的彈性, 可以避免更改其值後,可能相關程式都需要重新發佈的問題。

但以我自己來說,還是會把 readonly 視為唯讀而非常數,畢竟兩者在使用的根本上,有不同的目的。

我還是較傾向一篇國外文章( Const vs Static vs Readonly in C# )的說法 :
如果你確定這個數值永遠、永遠、永遠不會變動 ( 圓周率或是其他理科方面的公式常數 ),那你還是可以使用const。
但如果你不確定這個值會不會變動 ( 公司內部自定義之類 ),那保險起見,你還是用readonly。

小妹才疏學淺,以上內容若有任何錯誤或缺漏的地方,都歡迎大家分享或詢問~

***

另外如果有點進參考資料的話,應該會發現部分文章提及 get & enum

enum 類型可定義 整數內建類型 的具名常數 (例如 int、uint、long 等等)。

get 則是來自於 C# 3.0 的自動實作屬性 (auto implemented properties)

因為此篇已經太長了,連我自己都受不了,就留待之後另外發文了~

(欠文越來越多...)

終於收尾囉~

 


 

***來個懶人包***

  const Readonly
常數類型 靜態常數 動態常數
指派階段 編譯階段常數 執行階段常數
宣告型別 C#內建型別
如布林、數值和字串,及null參考
基本型別以至於類別、結構或陣列等
指派數值 宣告時就必須指派其數值 可在宣告 或 建構函式中 賦值
生命週期 可宣告常數欄位或區域常數 無法宣告為區域常數
靜態類別 是,預設static 否,預設非static
優  點 效能佳 有彈性

 


 

參考資料 : 

『Java』常數

[C#.NET] 定義常數時用 readonly 好? 還是 const 好?

Constants (C# Programming Guide)

常數const和static readonly

const (C# 參考)

How to define constants in C#

readonly (C# 參考)

CA2104:不要宣告唯讀的可變動參考類型

Difference between readonly and const keyword in C#

【C#】 定義常數時readonly好還是const好

編譯器錯誤 CS0133

C 常數運算式

C# Readonly - 教學筆記 (使用visual studio)

[C#] 006.區別readonly和const的使用方法 (程式有彈性用readonly,要效率用const)

Const vs Static vs Readonly in C#

'Static readonly' vs. 'const'

const 和 readonly 特性與使用時

C#基础知识系列八const和readonly关键字详细介绍

C# - const vs static readonly

Why are we allowed to use const with reference types if we may only assign null to them?

Immutable readonly reference types & FXCop Violation: Do not declare read only mutable reference types

關於Visual Studio 2008CCA2104-自動代碼分析不喜歡靜態唯讀可變類型

What is the difference between const and readonly in C#?

C# naming convention for constants?

Private Static Readonly Field Capitalization

Rules for Naming Constants – Aristides S. Bouras

arrow
arrow
    創作者介紹
    創作者 律晴音 的頭像
    律晴音

    聆風之境

    律晴音 發表在 痞客邦 留言(0) 人氣()