簡體   English   中英

“靜態只讀”與“常量”

[英]'Static readonly' vs. 'const'

我已經閱讀了有關conststatic readonly字段的信息。 我們有一些類只包含常量值。 它們用於我們系統中的各種事物。 所以我想知道我的觀察是否正確:

對於所有公開的內容,這些常量值是否應該始終為static readonly 並且僅將const用於internal / protected / private值?

你有什么建議嗎? 我是否應該甚至不使用static readonly字段,而是使用屬性?

public static readonly字段有點不尋常; public static屬性(只有一個get )會更常見(可能由private static readonly字段支持)。

const值直接燒入調用站點; 這是雙刃劍:

  • 如果該值是在運行時獲取的,可能是從配置中獲取的,則它是無用的
  • 如果你改變了一個 const 的值,你需要重建所有的客戶端
  • 但它可以更快,因為它避免了方法調用......
  • ...無論如何有時可能已被 JIT 內聯

如果值永遠不會改變,那么 const 就可以了 - Zero等構成合理的常量 ;p 除此之外, static屬性更常見。

如果消費者在不同的程序集中,我將使用static readonly constConsumer放在兩個不同的程序集中是一種很好的打擊自己的方式。

還有一些需要注意的相關事項:

const int a

  • 必須初始化。
  • 初始化必須在編譯時進行

只讀 int a

  • 可以使用默認值,無需初始化。
  • 初始化可以在運行時完成(編輯:僅在構造函數中)。

這只是對其他答案的補充。 我不會重復它們(現在四年后)。

在某些情況下, const和非常量具有不同的語義。 例如:

const int y = 42;

static void Main()
{
  short x = 42;
  Console.WriteLine(x.Equals(y));
}

打印出True ,而:

static readonly int y = 42;

static void Main()
{
  short x = 42;
  Console.WriteLine(x.Equals(y));
}

False

原因是方法x.Equals有兩個重載,一個接受short ( System.Int16 ),另一個接受一個object ( System.Object )。 現在的問題是一個或兩個是否適用於我的y論點。

y是一個編譯時間常數(字面),則const的情況下,它有不存在的隱式轉換變得重要intshort規定, int是一個常數,並且條件是C#編譯器驗證它的值是內short的范圍( 42是)。 請參閱 C# 語言規范中的隱式常量表達式轉換 所以這兩種重載都必須考慮。 重載Equals(short)是首選(任何short都是object ,但並非所有object都是short )。 所以y被轉換為short ,並且使用了那個重載。 然后Equals比較兩個short的相同值,並給出true

y不是常數時,不存在從intshort隱式轉換。 那是因為通常int可能太大而無法放入short (確實存在顯式轉換,但我沒有說Equals((short)y) ,所以這無關緊要。)我們看到只有一個重載適用,即Equals(object)一個。 所以y被裝箱到object 然后Equals會將System.Int16System.Int32進行比較,並且由於運行時類型甚至不一致,這將產生false

我們得出結論,在某些(罕見)情況下,將const類型成員更改為static readonly字段(或其他方式,如果可能)可以更改程序的行為。

需要注意的一件事是const僅限於原始/值類型(字符串除外)。

靜態只讀

可以在運行時通過static構造函數更改該值。 但不是通過成員函數。

常數

默認情況下是static 不能從任何地方(構造函數、函數、運行時等)更改值。

只讀

可以在運行時通過構造函數更改該值。 但不是通過成員函數。

您可以查看我的存儲庫: C# 屬性類型

readonly關鍵字與const關鍵字不同。 const字段只能在字段聲明時初始化。 readonly字段可以在聲明或構造函數中初始化。 因此, readonly字段可以根據使用的構造函數具有不同的值。 此外,雖然const字段是編譯時常量,但readonly字段可用於運行時常量

簡短明了的 MSDN 參考在這里

constreadonly很相似,但它們並不完全相同。

const字段是編譯時常量,這意味着可以在編譯時計算該值。 readonly字段支持在類型的構造期間必須運行某些代碼的其他場景。 構建后, readonly字段不能更改。

例如, const成員可用於定義如下成員:

struct Test
{
    public const double Pi = 3.14;
    public const int Zero = 0;
}

因為像 3.14 和 0 這樣的值是編譯時常量。 但是,請考慮您定義類型並希望提供它的一些預制實例的情況。 例如,您可能想要定義一個 Color 類並為常見顏色(如黑色、白色等)提供“常量”。使用 const 成員是不可能做到這一點的,因為右側不是編譯時常量。 可以使用常規靜態成員來做到這一點:

public class Color
{
    public static Color Black = new Color(0, 0, 0);
    public static Color White = new Color(255, 255, 255);
    public static Color Red   = new Color(255, 0, 0);
    public static Color Green = new Color(0, 255, 0);
    public static Color Blue  = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}

但是沒有什么可以阻止 Color 的客戶端使用它,也許是通過交換 Black 和 White 值。 不用說,這會引起 Color 類的其他客戶的驚愕。 “只讀”功能解決了這種情況。

通過在聲明中簡單地引入readonly關鍵字,我們保留了靈活的初始化,同時防止客戶端代碼亂七八糟。

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red   = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue  = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}

有趣的是,const 成員總是靜態的,而 readonly 成員可以是靜態的,也可以不是,就像常規字段一樣。

可以將單個關鍵字用於這兩個目的,但這會導致版本問題或性能問題。 假設我們為此(const)使用了一個關鍵字,並且開發人員寫道:

public class A
{
    public static const C = 0;
}

另一個開發人員編寫了依賴於 A 的代碼:

public class B
{
    static void Main() => Console.WriteLine(A.C);
}

現在,生成的代碼能否依賴於 AC 是編譯時常量這一事實? 即,AC 的使用可以簡單地替換為值 0 嗎? 如果你對此說“是”,那么這意味着 A 的開發者不能改變 AC 的初始化方式——這在未經許可的情況下束縛了 A 的開發者的手。

如果你對這個問題說“不”,那么就錯過了一個重要的優化。 也許 A 的作者肯定 AC 永遠為零。 const 和 readonly 的使用允許 A 的開發人員指定意圖。 這有助於實現更好的版本控制行為和更好的性能。

我的偏好是盡可能使用const ,正如前面的答案中提到的,它僅限於文字表達式或不需要評估的東西。

如果我遇到了這個限制,那么我會退回到static readonly ,但有一個警告。 我通常會使用帶有 getter 的公共靜態屬性和支持私有靜態只讀字段,正如 Marc 在此處提到的那樣。

Const: Const 只不過是“常量”,一個變量,其值是常量,但在編譯時。 並且必須為其分配一個值。 默認情況下,const 是靜態的,我們不能在整個程序中更改 const 變量的值。

靜態只讀:靜態只讀類型變量的值可以在運行時分配或在編譯時分配並在運行時更改。 但是這個變量的值只能在靜態構造函數中改變。 並且無法進一步更改。 它只能在運行時更改一次

參考: c-sharpcorner

當向其他程序集公開可能在更高版本中更改的值時,靜態只讀字段是有利的。

例如,假設程序集X公開一個常量,如下所示:

public const decimal ProgramVersion = 2.3;

如果程序集Y引用X並使用此常量,則值 2.3 將在編譯時烘焙到程序集Y 這意味着如果X稍后在常量設置為 2.4 的情況下重新編譯,則Y仍將使用舊值 2.3,直到重新編譯Y 靜態只讀字段避免了這個問題。

另一種看待這個問題的方式是,任何可能在未來發生變化的值在定義上都不是恆定的,因此不應表示為一個。

Const :常量變量值必須與聲明一起定義,之后它不會改變。 const 是隱式靜態的,因此無需創建類實例我們就可以訪問它們。 這在編譯時有一個值。

ReadOnly :我們可以在聲明以及在運行時使用構造函數時定義只讀變量值。 沒有類實例就不能訪問只讀變量。

靜態只讀:我們可以在聲明時定義靜態只讀變量值,並且只能通過靜態構造函數,但不能使用任何其他構造函數。 我們也可以在不創建類實例的情況下訪問這些變量(作為靜態變量)。

如果我們必須使用不同程序集中的變量,靜態只讀將是更好的選擇。 請查看以下博客文章中的完整詳細信息:

Const Strings – 一種非常方便的用腳射擊自己的方法

C#.Net 中的 const 和靜態只讀字段之間存在細微差別

const 必須在編譯時用值初始化。

const 默認是靜態的,需要用常量值初始化,以后不能修改。 它不能與所有數據類型一起使用。 對於前日期時間。 它不能與 DateTime 數據類型一起使用。

public const DateTime dt = DateTime.Today;  //throws compilation error
public const string Name = string.Empty;    //throws compilation error
public static readonly string Name = string.Empty; //No error, legal

readonly 可以聲明為靜態,但不是必需的。 無需在聲明時初始化。 可以使用構造函數一次分配或更改其值。 所以有可能改變 readonly 字段的值一次(無關緊要,如果它是靜態的),這是 const 不可能的。

常量:

  1. 值應在聲明時給出
  2. 編譯時間常數

只讀:

  1. 值可以在聲明時或在運行時使用構造函數給出。該值可能因所使用的構造函數而異。
  2. 運行時間常數

const(在編譯時確定)可用於只讀靜態不能的情況,例如在 switch 語句或屬性構造函數中。 這是因為只讀字段僅在運行時解析,並且某些代碼構造需要編譯時保證。 可以在構造函數中計算只讀靜態,這通常是必不可少且有用的東西。 區別是功能性的,在我看來它們的用法應該如此。

在內存分配方面,至少對於字符串(作為引用類型),兩者似乎沒有區別,因為兩者都被實習並且將引用一個實習實例。

就我個人而言,我的默認值是只讀靜態的,因為它對我來說更具語義和邏輯意義,特別是因為在編譯時不需要大多數值。 而且,順便說一句,公共只讀靜態並不罕見或不常見,如標記答案所述:例如, System.String.Empty就是其中之一。

聲明conststatic readonly 的另一個區別在於內存分配。

靜態字段屬於對象的類型而不是該類型的實例。 結果,一旦第一次引用該類,靜態字段將在剩余時間內“存在”在內存中,並且該類型的所有實例都將引用靜態字段的同一個實例。

另一方面, const字段“屬於該類型的一個實例。

如果釋放內存對您來說更重要,則更喜歡使用const 如果速度,則使用static readonly

常量就像名字暗示的那樣,字段不會改變並且通常在編譯時在代碼中靜態定義。

只讀變量是可以在特定條件下更改的字段。

它們可以在您第一次像常量一樣聲明它們時被初始化,但通常它們是在構造函數內部的對象構造過程中初始化的。

在上述條件下,它們在初始化發生后不能更改。

靜態只讀對我來說聽起來是一個糟糕的選擇,因為如果它是靜態的並且永遠不會改變,那么只需使用 public const。 如果它可以改變,那么它就不是一個常量,然后,根據您的需要,您可以使用只讀變量或僅使用常規變量。

另外,另一個重要的區別是常量屬於類,而只讀變量屬於實例!

有一個重要的問題,在上面的答案中沒有提到,應該促使您更喜歡“const”,尤其是對於“int”、“string”等基本類型。

常量可以作為屬性參數,靜態只讀字段不行!

Azure 函數 HttpTrigger,未在屬性中使用 HttpMethods 類

如果只有微軟使用 Http 的 GET、POST、DELETE 等常量。

應該可以寫

[HttpTrigger(AuthorizationLeve.Anonymous,  HttpMethods.Get)] // COMPILE ERROR: static readonly, 

但我不得不求助於

[HttpTrigger(AuthorizationLeve.Anonymous,  "GET")] // STRING

或者使用我自己的常量:

public class HttpConstants
{
    public const string Get = "GET";
}

[HttpTrigger(AuthorizationLeve.Anonymous,  HttpConstants.Get)] // Compile FINE!

如果可以提供編譯時常量,請使用const

private const int Total = 5;

如果您需要在運行時評估您的值,請使用static readonly

private static readonly int GripKey = Animator.StringToHash("Grip");

這將產生編譯錯誤,因為在編譯時無法獲得該值。

private const int GripKey = Animator.StringToHash("Grip");

常量

  1. 只能應用於字段。 值應該在代碼編譯時。
  2. 適合在編譯代碼之前在代碼中刪除魔術“字符串”、“int/double”、(原始類型)等。
  3. 編譯后,該值將放置在使用常量的所有編譯代碼中。 所以如果你有一個巨大的字符串在很多地方使用,那么在使它成為 const 之前要小心。 考慮使用靜態只讀。

靜態只讀

  1. 靜態只讀適用於字段/道具,靜態可用於方法。 (附注)當靜態應用於方法時,編譯后的代碼不會將“this”參數傳遞給方法,因此您無法訪問對象的實例數據。
  2. 適用於編譯代碼后可能更改的值。 像從配置中初始化的值,在應用程序啟動期間等。
  3. 編譯代碼后,在 IL 代碼中使用 ref to value 可能比使用 const 慢,但編譯后的代碼很小

在重構期間,所有 const 都可以安全地轉換為靜態只讀,但反之亦然,正如我們在上面看到的,當轉換的代碼可能會中斷,因為可以在構造函數中初始化一些靜態只讀變量。

上面提到的另一個我不相信的區別是:

conststatic readonly值不會在 Visual Studio IDE中應用 CodeLens。

static僅獲取屬性 DO 將 CodeLens 應用於它們。

代碼鏡頭示例

我認為添加 CodeLens 非常有價值。

注意:當前使用 Visual Studio 2022。

Const, readonly, static readonly - 執行類似操作但有重要區別的關鍵字:

Const -是一個變量,其值為常量並在編譯時分配。 您必須為其分配一個值。 默認常量是static,我們不能在整個程序中改變const變量的值。

Readonly -表示我們可以在運行時更改的值,或者我們可以在運行時分配它,但只能通過非靜態構造函數。

Static 只讀-值可以在運行時分配或在編譯時分配並在運行時更改。 但是這個變量的值只能在 static 構造函數中改變。 並且無法進一步更改。 它只能在執行期間更改一次。

您可以在這里找到示例 - https://www.c-sharpcorner.com/UploadFile/c210df/difference-between-const-readonly-and-static-readonly-in-C-Sharp/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM