Разъяснение сгенерированного кода IL, ссылающегося на строки - PullRequest
0 голосов
/ 13 декабря 2018

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

Пожалуйста, рассмотрите этот простой пример

 class Program
{
    public static readonly string a = "a";
    public const string b = "b";
    static void Main(string[] args)
    {

        Console.WriteLine(a);

        Console.WriteLine(b);
    }
}

Теперь, если я посмотрю на сгенерированный IL-код (полученный через IL Browser от resharp)

Я вижу следующий код

.method private hidebysig static void 
Main(
  string[] args
) cil managed 
{
 .entrypoint
.maxstack 8

// [16 13 - 16 34]
IL_0000: ldsfld       string ConsoleApp4.Program::a
IL_0005: call         void [mscorlib]System.Console::WriteLine(string)

// [18 13 - 18 34]
IL_000a: ldstr        "b"
IL_000f: call         void [mscorlib]System.Console::WriteLine(string)

// [19 9 - 19 10]
IL_0014: ret          

 } // end of method Program::Main

 .method public hidebysig specialname rtspecialname instance void 
.ctor() cil managed 
{
 .maxstack 8

IL_0000: ldarg.0      // this
IL_0001: call         instance void [mscorlib]System.Object::.ctor()
IL_0006: ret          

 } // end of method Program::.ctor

 .method private hidebysig static specialname rtspecialname void 
.cctor() cil managed 
 {
.maxstack 8

// [11 9 - 11 47]
IL_0000: ldstr        "a"
IL_0005: stsfld       string ConsoleApp4.Program::a
IL_000a: ret          

 } // end of method Program::.cctor
  } // end of class ConsoleApp4.Program

Что касается статической строки, она ведет себя так, как я ожидал.Вместо этого для const он загружает новое значение в стек ... фактически смотрит на код операции ldstr здесь он говорит:

Добавляет новую ссылку на объект на строковый литерал, хранящийся вметаданные

Я прочитал здесь , что

Теперь, куда бы ни ссылался myInt в коде, вместо того, чтобы делать "ldloc".0 ", чтобы получить значение из переменной, MSIL просто загружает постоянное значение, которое жестко закодировано в MSIL.Таким образом, использование констант обычно дает небольшое преимущество в производительности и памяти. Однако для их использования необходимо иметь значение переменной во время компиляции и любые ссылки на эту константу во время компиляции, даже если они находятся вв другой сборке будет произведена замена.

Константы, безусловно, полезный инструмент, если вы знаете значение во время компиляции.Если вы этого не сделаете, но хотите убедиться, что ваша переменная установлена ​​только один раз, вы можете использовать ключевое слово readonly в C # (которое сопоставляется с initonly в MSIL), чтобы указать, что значение переменной можно установить только в конструкторе;после этого будет ошибкой изменить его.Это часто используется, когда поле помогает определить идентичность класса, и часто устанавливается равным параметру конструктора.

Но почему я должен испытывать более высокую производительность?(даже если учесть, что это довольно подлый)?А как насчет памяти?

Заранее спасибо

1 Ответ

0 голосов
/ 17 декабря 2018

Рассмотрим этот код:

public class Program
{
    public const int ConstField1 = 1;
    public const int ConstField2 = 2;
    public const int ConstField3 = 3;
    public const int ConstField4 = 4;
}

Четыре числа const int32 хранятся только в памяти, которая соответствует метаданным сборки (поэтому они могут быть получены с помощью отражения), но не в фактической работе.информация о типе времени.По сравнению с static readonly это экономит 16 байт памяти.В случае строк среда выполнения также не должна выделять строку, прежде чем она будет фактически использована в другом коде (поскольку ldstr не используется для инициализации поля).Вы можете утверждать, что это не сильно экономит, но рассмотрите перечисления - это в основном статические типы с большим количеством константных полей.

Улучшение производительности также очевидно - поскольку нет необходимости каждый раз извлекать значениеон используется, фрагментация памяти уменьшается, и другие значения могут быть выполнены для значения, которое невозможно было бы использовать в других случаях (например, упрощающие выражения, такие как BindingFlags.NonPublic | BindingFlags.Instance).Также нет необходимости вызывать статический конструктор, так что это еще одна точка (хотя в некоторых случаях она может не вызываться, см. beforefieldinit).

...