c#?中只读关键字/表达式主体成员之间的区别,哪个更好?

时间:2018-08-10 06:58:24

标签: c# c#-6.0

在c#中,只读成员可以减少为不可变成员的只读自动属性/表达式强成员,而表达式强成员比使用readonly关键字更好吗?

使用只读键:

public static readonly string  COMPANY_NAME= "XYZ";

使用表情健全的成员

public  static  string  COMPANY_NAME => "XYZ";

我遇到过各种论坛和解决方案,这些建议建议表达强壮的成员以供短手使用,但我找不到它的性能差异。

3 个答案:

答案 0 :(得分:17)

让我们深入研究一下,看看编译器对不同类型的字段做了什么。

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)

表达式主体成员生成一个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"; } }会导致方法调用返回一个常量,但是看起来好像需要额外的内存分配

实际上,性能差异可能是不可观察的。如前所述,IL代码可以通过JIT进一步优化,因此您可以有效地获得相同的性能。 不过,我更喜欢选择选项1或2。

答案 1 :(得分:9)

首先,您应该将conststring常量一起使用,而不要与readonly一起使用。您应该仅将后者用于需要构造函数调用来构造它们的对象。

不过,有一条旁注,如注释中所述,您应该意识到即使在程序集之间也可以优化常量(因此,库常量也可以在引用时被库视为编译时的常量)。这意味着在进行次要版本更新时,程序集中可能会在库中得到另一个常数值。在这种情况下,您应该继续使用static readonly

第二,static readonly字段和static属性之间存在巨大差异。每次调用static属性都会对其进行评估。 static readonly略有优化,因为它仅执行一次分配。

答案 2 :(得分:7)

在这种情况下,总体结果 看起来是一样的,但要意识到它们是完全不同的。

第一个定义一个readonly字段。 =右侧的初始化表达式运行一次,并且该字段始终返回该值。

第二个定义了仅get的属性。 =>右侧的表达式将每次被访问。

在这种情况下,您的表达式是确定性的,并产生不可变的对象。如果这两个都不成立,那么可以观察到它们之间的差异(通过第二个返回不同的结果,或者可以修改第一个的内容)