Подтвердить что ты не робот

Разница между ключевым словом readonly/Expression-bodied членами в С#?, что лучше?

В С# readonly члены могут быть сведены к readonly auto properties/expression-bodied members для неизменяемых членов, а члены с выраженным состоянием лучше использовать ключевые слова для чтения?

Использование readonly keywork:

public static readonly string  COMPANY_NAME= "XYZ";

Использование выражений:

public  static  string  COMPANY_NAME => "XYZ";

Я столкнулся с различными форумами и решениями, которые предполагают, что члены-участники могут использовать короткие руки, и я не мог найти, как он отличается от производительности.

4b9b3361

Ответ 1

Дайте глубоко погрузиться и посмотрите, что делает компилятор для разных типов полей.

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)

Элемент выражения с выражением генерирует геттер, который возвращает постоянное значение:

.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 с инициализацией генерирует дополнительное поле поддержки для инициализирующего значения.

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

Свойство getter с полным телом немного дольше:

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

Ответ 2

Во-первых, вы должны использовать const со string константами, а не только для readonly. Вы должны использовать последний только для объектов, требующих вызова конструктора для их создания.

Для этого есть примечание, но, как указано в комментариях, вы должны знать, что константы будут оптимизированы даже по сборкам (поэтому ваши константы библиотеки также могут быть оценены как константы во время компиляции по ссылке библиотеки). Это означало бы, что с незначительными обновлениями версий вы могли бы получить другое постоянное значение в сборке, чем в своей библиотеке. В таких случаях вы должны продолжать использовать static readonly.

Во-вторых, существует огромная разница между static readonly полями static readonly и static свойствами. static свойства будут оцениваться каждый раз, когда вы его вызываете. static readonly немного оптимизирован, поскольку он выполняет только одно назначение.

Ответ 3

В этом случае общие результаты будут одинаковыми, но поймут, что они совершенно разные.

Первый определяет поле только для readonly. Выражение инициализации справа от = запускается один раз, и поле всегда возвращает это значение.

Второй определяет свойство get -only. Выражение справа от => будет оцениваться каждый раз, когда к нему обращаются.

В этом случае ваши выражения детерминированы и создают неизменные объекты. Если бы ни одно из них не было истинным, то разница между ними была бы наблюдаемой (второй возвращал бы разные результаты или мог бы модифицировать содержимое первого)