Сравнение readonly и const

Сравнение readonly и const

Необходимость использования неизменяемого объекта возникает всегда. Для этого в языке C# используется два ключевых слова readonly и const. Однако не все так просто при их использовании и нужно понимать некоторые различия.

Прежде всего readonly используется с полями, таким образом у нас всегда есть экземпляр значимого типа или ссылка на ссылочный тип. Единственное ограничение, которое накладывает на нас компилятор — это объявление readonly-поля непосредственно в коде или в конструкторе.

class Program
{
    private readonly int value1 = 12;
    private readonly int value2;
    private readonly int value3;

    public Program()
    {
        this.value2 = 13;
        this.InitValue3();
    }

    private void InitValue3()
    {
        this.value3 = 14; // Ошибка компиляции
    }
}

const используется для объявления констант, т.е. неизменяемых идентификаторов. Её основное отличие от поля в том, что компилятор должен знать значение константы уже на этапе компиляции, чтобы внести эти значения в метаданные сборки.

class Program
{
    private const int value1 = 12;
    private const int value2; // Ошибка компиляции, так как значение константы не задано
}

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

public class Resources
{
	public const int ErrorConst = 1;
	public static readonly int ErrorField = 2;
}

В вызывающей программе мы используем эти ресурсы следующим образом.

class Program
{
	static void Main(string[] args)
	{
		Console.WriteLine(DataLib.Resources.ErrorConst);
		Console.WriteLine(DataLib.Resources.ErrorField);
	}
}

Ничего сложного просто выводим два числа на консоль. Воспользуемся IL DASM и изучим код нашего приложения.

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       20 (0x14)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldc.i4.1
  IL_0002:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0007:  nop
  IL_0008:  ldsfld     int32 [DataLib]DataLib.Resources::ErrorField
  IL_000d:  call       void [mscorlib]System.Console::WriteLine(int32)
  IL_0012:  nop
  IL_0013:  ret
} // end of method Program::Main

Нужный нам код начинается со строки IL_0001 где инструкция ldc помещает число 1 в стек. Как видим здесь нет никакого указания на саму константу или библиотеку в которой она находится. Далее в строке IL_0002 идет вывод на консоль.

Работа со статическим полем идет схожим образом. Первым делом мы помещаем его в стек командой ldsfld и затем выводим на консоль. Однако в отличии от работы с константой за значением поля мы обращаемся к нашей библиотеке DataLib.

Этот небольшой IL-код показывается фундаментальное различие между полями и константами. На этапе компиляции компилятор берет значения констант и вставляет их в нужные места, тогда как для работы с полями нам просто достаточно ссылки. Таким образом при работе с константами, размещенными в других библиотеках мы должны всегда быть уверены, что при сборке у нас используется самая последняя версия.

Комментарии

Оставить комментарий