简体   繁体   English

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

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

In c# readonly members can be reduced to readonly auto properties/expression-bodied members for immutable members is expression-bodied members are better then using readonly keywords? 在c#中,readonly成员可以简化为只读自动属性/表达式成员,对于不可变成员,表达式成员比使用readonly关键字更好吗?

Using readonly keywork: 使用readonly keywork:

public static readonly string  COMPANY_NAME= "XYZ";

Using Expression-bodied Members: 使用表达身体的成员:

public  static  string  COMPANY_NAME => "XYZ";

I have come across various forums and solution that suggest expression bodied members for short hand use, and i could not find how it differs in performance. 我遇到过各种论坛和解决方案,建议表达身体的成员用于短手使用,我无法找到它在性能上的差异。

Let's dive deep and see what compiler does for different types of fields. 让我们深入了解一下编译器对不同类型的字段所做的工作。

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 creates a literal string and will be evaluated at call site: 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 creates a field that is initialized in ctor and means only one field reference when used: 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 member generates a getter, which returns constant value: 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)

Readonly property with initialization generates an additional backing field for the initializing value. 初始化的只读属性会为初始化值生成一个额外的支持字段。

.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)

A Property getter with full body is a bit longer: 具有全身的属性获取器有点长:

.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)

From that, we can order them by amount of code (and calls) they generate: 从那以后,我们可以通过他们生成的代码(和调用)来订购它们:

  • const string is definitely the fastest one, but can cause unexpected behavior in case of change, when used from other assembiles (as other answers mentioned) const string绝对是最快的,但是在更改的情况下可能会导致意外行为,当从其他集合中使用时(如提到的其他答案)
  • static readonly comes right behind, with one field access static readonly后面,一个字段访问
  • static string ExpressionBodied => "xxx" will cause a method call (getter) that simply returns a constant static string ExpressionBodied => "xxx"将导致只返回常量的方法调用(getter)
  • static string GetInitialized {get;} = "xxx" will result in a method call and a field access static string GetInitialized {get;} = "xxx"将导致方法调用和字段访问
  • static string GetWithBody { get { return "xxx"; } } static string GetWithBody { get { return "xxx"; } } will cause a method call that returns a constant, but wih additional memory allocation, it would seem static string GetWithBody { get { return "xxx"; } }会导致一个返回常量的方法调用,但是看起来还有额外的内存分配

In practice, the performance differences will probably be unobservable. 在实践中,性能差异可能是不可观察的。 As pointed out, IL code can be further optimized by JIT, so you can end up with effectively the same performance. 正如所指出的,IL代码可以通过JIT进一步优化,因此您可以最终获得相同的性能。 Nevertheless, I prefer to go with option 1. or 2. 不过,我更喜欢选择1.或2。

First, you should use const with string constants, not readonly . 首先,你应该使用conststring常量,而不是readonly You should use the latter only for objects requiring a constructor call to construct them. 您应该仅将后者用于需要构造函数调用来构造它们的对象。

There is a side note to this though, as stated in comments, you should be aware that constants will be optimized even across assemblies (so your library constants can also be evaluated as constants on compile time by the referenced library). 这里有一个注意事项,正如注释中所述,你应该知道常量甚至会跨程序集进行优化(因此你的库常量也可以在引用库的编译时被评估为常量)。 That would mean that with minor version updates, you could end up with another constant value in the assembly than in your library. 这意味着,通过次要版本更新,您可能最终在程序集中使用另一个常量值,而不是在库中。 In such cases you should keep using static readonly . 在这种情况下,您应该继续使用static readonly

Second, there is a huge difference between static readonly fields and static properties. 其次, static readonly字段和static属性之间存在巨大差异。 The static properties will get evaluated each and every time you call it. 每次调用static属性时都会对其进行评估。 The static readonly is slightly optimized, since it does only do a single assignment. static readonly稍微优化,因为它只进行一次分配。

In this case, the overall results will appear the same, but realise that they are quite different. 在这种情况下,整体结果将显得相同,但意识到它们是完全不同的。

The first defines a readonly field. 第一个定义了一个readonly字段。 The initialization expression to the right of the = runs once, and the field always returns that value. =右边的初始化表达式运行一次,字段始终返回该值。

The second defines a get -only property. 第二个定义了一个get -only属性。 The expression to the right of the => will be evaluated each time it is accessed. 每次访问 ,都会评估=>右侧的表达式。

In this case, your expressions are deterministic and produce immutable objects. 在这种情况下,表达式是确定性的并生成不​​可变对象。 If neither of these were true, then the difference between them would be observable (by the second returning different results or by being able to modify the contents of the first) 如果这些都不是真的,那么它们之间的差异将是可观察的(通过第二次返回不同的结果或者能够修改第一个的内容)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM