簡體   English   中英

c#中readonly關鍵字/表達式成員之間的區別,哪個更好?

[英]Difference between readonly keyword/Expression-bodied members in c#?, which is better?

在c#中,readonly成員可以簡化為只讀自動屬性/表達式成員,對於不可變成員,表達式成員比使用readonly關鍵字更好嗎?

使用readonly keywork:

public static readonly string  COMPANY_NAME= "XYZ";

使用表達身體的成員:

public  static  string  COMPANY_NAME => "XYZ";

我遇到過各種論壇和解決方案,建議表達身體的成員用於短手使用,我無法找到它在性能上的差異。

讓我們深入了解一下編譯器對不同類型的字段所做的工作。

class Program
{
    public const string ConstString = "mesa const";
    public static readonly string ReadonlyStatic = "mesa readonly";
    public static string ExpressionBodied => "mesa expression";
    public static string GetInitialized {get;} =  "mesa get";
    public static string GetWithBody { get { return "mesa get"; } } 

    static void Main(string[] args)
    {
        Console.WriteLine("Hello World!");

        System.Console.WriteLine("readonly:" + ReadonlyStatic);
        System.Console.WriteLine("const:" + ConstString);
        System.Console.WriteLine("expression bodied:" + ExpressionBodied);
        System.Console.WriteLine("get initialized:" + GetInitialized);
        System.Console.WriteLine("get with body:" + GetWithBody);
    }
}

const string創建一個literal string ,將在調用站點進行評估:

.field public static literal string ConstString = "mesa const"

// call site:
IL_0021: ldstr        "const:mesa const"
IL_0026: call         void [System.Console]System.Console::WriteLine(string)

static readonly創建一個在ctor中初始化的字段,並且在使用時僅表示一個字段引用:

.field public static initonly string ReadonlyStatic

// call site:
IL_000c: ldstr        "readonly:"
IL_0011: ldsfld       string readonly_props.Program::ReadonlyStatic
IL_0016: call         string [System.Runtime]System.String::Concat(string, string)
IL_001b: call         void [System.Console]System.Console::WriteLine(string)

Expression bodied成員生成一個getter,它返回常量值:

.method public hidebysig static specialname string 
get_ExpressionBodied() cil managed 
{
  .maxstack 8

  // [9 50 - 9 67]
  IL_0000: ldstr        "mesa expression"
  IL_0005: ret          
} // end of method Program::get_ExpressionBodied

// call site:
IL_002c: ldstr        "expression bodied:"
IL_0031: call         string readonly_props.Program::get_ExpressionBodied()
IL_0036: call         string [System.Runtime]System.String::Concat(string, string)
IL_003b: call         void [System.Console]System.Console::WriteLine(string)

初始化的只讀屬性會為初始化值生成一個額外的支持字段。

.field private static initonly string '<GetInitialized>k__BackingField'    
.method public hidebysig static specialname string 
  get_GetInitialized() cil managed 
{
  .custom instance void [System.Runtime]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() 
  = (01 00 00 00 )
  .maxstack 8
  // [10 46 - 10 50]
  IL_0000: ldsfld       string readonly_props.Program::'<GetInitialized>k__BackingField'
  IL_0005: ret          
} // end of method Program::get_GetInitialized

// call site:
IL_0041: ldstr        "get initialized:"
IL_0046: call         string readonly_props.Program::get_GetInitialized()
IL_004b: call         string [System.Runtime]System.String::Concat(string, string)
IL_0050: call         void [System.Console]System.Console::WriteLine(string)

具有全身的屬性獲取器有點長:

.method public hidebysig static specialname string 
  get_GetWithBody() cil managed 
{
  .maxstack 1
  .locals init (
    [0] string V_0
  )

  // [11 48 - 11 49]
  IL_0000: nop          

  // [11 50 - 11 68]
  IL_0001: ldstr        "mesa get"
  IL_0006: stloc.0      // V_0
  IL_0007: br.s         IL_0009

  // [11 69 - 11 70]
  IL_0009: ldloc.0      // V_0
  IL_000a: ret          

} // end of method Program::get_GetWithBody

// call site:
IL_0056: ldstr        "get with body:"
IL_005b: call         string readonly_props.Program::get_GetWithBody()
IL_0060: call         string [System.Runtime]System.String::Concat(string, string)
IL_0065: call         void [System.Console]System.Console::WriteLine(string)

從那以后,我們可以通過他們生成的代碼(和調用)來訂購它們:

  • const string絕對是最快的,但是在更改的情況下可能會導致意外行為,當從其他集合中使用時(如提到的其他答案)
  • static readonly后面,一個字段訪問
  • static string ExpressionBodied => "xxx"將導致只返回常量的方法調用(getter)
  • static string GetInitialized {get;} = "xxx"將導致方法調用和字段訪問
  • static string GetWithBody { get { return "xxx"; } } static string GetWithBody { get { return "xxx"; } }會導致一個返回常量的方法調用,但是看起來還有額外的內存分配

在實踐中,性能差異可能是不可觀察的。 正如所指出的,IL代碼可以通過JIT進一步優化,因此您可以最終獲得相同的性能。 不過,我更喜歡選擇1.或2。

首先,你應該使用conststring常量,而不是readonly 您應該僅將后者用於需要構造函數調用來構造它們的對象。

這里有一個注意事項,正如注釋中所述,你應該知道常量甚至會跨程序集進行優化(因此你的庫常量也可以在引用庫的編譯時被評估為常量)。 這意味着,通過次要版本更新,您可能最終在程序集中使用另一個常量值,而不是在庫中。 在這種情況下,您應該繼續使用static readonly

其次, static readonly字段和static屬性之間存在巨大差異。 每次調用static屬性時都會對其進行評估。 static readonly稍微優化,因為它只進行一次分配。

在這種情況下,整體結果將顯得相同,但意識到它們是完全不同的。

第一個定義了一個readonly字段。 =右邊的初始化表達式運行一次,字段始終返回該值。

第二個定義了一個get -only屬性。 每次訪問 ,都會評估=>右側的表達式。

在這種情況下,表達式是確定性的並生成不​​可變對象。 如果這些都不是真的,那么它們之間的差異將是可觀察的(通過第二次返回不同的結果或者能夠修改第一個的內容)

暫無
暫無

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

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