Глава 7.
Константы и поля
В этой главе показано, как добавить к типу члены, содержащие данные. В частности,
мы рассмотрим константы и поля.
Константы
Константа
(constant) — это идентификатор, значение которого никогда не ме-
няется. Значение, связанное с именем константы, должно определяться во время
компиляции. Затем компилятор сохраняет значение константы в метаданных
модуля. Это значит, что константы можно определять только для таких типов,
которые компилятор считает примитивными. В C# следующие типы считаются
примитивными и могут использоваться для определения констант:
Boolean
,
Char
,
Byte
,
SByte
,
Int16
,
UInt16
,
Int32
,
UInt32
,
Int64
,
UInt64
,
Single
,
Double
,
Decimal
и
String
. Тем не менее C# позволяет определить константную переменную, не от-
носящуюся к элементарному типу, если присвоить ей значение
null
:
using System;
public sealed class SomeType {
// Некоторые типы не являются элементарными, но С# допускает существование
// константных переменных этих типов после присваивания значения null
public const SomeType Empty = null;
}
Так как значение констант никогда не меняется, константы всегда считаются
частью типа. Иначе говоря, константы считаются статическими, а не экземпляр-
ными членами. Определение константы приводит в конечном итоге к созданию
метаданных.
Встретив в исходном тексте имя константы, компилятор просматривает метадан-
ные модуля, в котором она определена, извлекает значение константы и внедряет
его в генерируемый им IL-код. Поскольку значение константы внедряется прямо в
код, в период выполнения память для констант не выделяется. Кроме того, нельзя
получать адрес константы и передавать ее по ссылке. Эти ограничения также озна-
чают, что изменять значения константы в разных версиях модуля нельзя, поэтому
константу надо использовать, только когда точно известно, что ее значение никогда
не изменится (хороший пример — определение константы
MaxInt16
со значением
32767). Поясню на примере, что я имею в виду. Возьмем код и скомпилируем его
в DLL-сборку:
211
Константы
using System;
public sealed class SomeLibraryType {
// ПРИМЕЧАНИЕ: C# не позволяет использовать для констант модификатор
// static, поскольку всегда подразумевается, что константы являются
// статическими
public const Int32 MaxEntriesInList = 50;
}
Затем построим сборку приложения из следующего кода:
using System;
public sealed class Program {
public static void Main() {
Console.WriteLine("Max entries supported in list: "
+ SomeLibraryType.MaxEntriesInList);
}
}
Нетрудно заметить, что код приложения содержит ссылку на константу
MaxEntriesInList
. При компоновке этого кода компилятор, обнаружив, что
MaxEntriesInList
— это литерал константы со значением 50, внедрит значение 50
типа
Int32
прямо в IL-код приложения. Фактически после построения кода при-
ложения DLL-сборка даже не будет загружаться в период выполнения, поэтому ее
можно просто удалить с диска.
.method public hidebysig static void Main() cil managed
{
.entrypoint
// Code size 25 (0x19)
.maxstack 8
IL_0000: nop
IL_0001: ldstr "Max entries supported in list: "
IL_0006: ldc.i4.s 50
IL_0008: box [mscorlib]System.Int32
IL_000d: call string [mscorlib]System.String::Concat(object, object)
IL_0012: call void [mscorlib]System.Console::WriteLine(string)
IL_0017: nop
IL_0018: ret
} // Закрываем метод Program::Main
Теперь проблема управления версиями при использовании констант должна
стать очевидной. Если разработчик изменит значение константы
MaxEntriesInList
на 1000 и перестроит только DLL-сборку, это не повлияет на код самого прило-
жения. Для того чтобы в приложении использовалось новое значение константы,
его тоже необходимо перекомпилировать. Нельзя применять константы во время
выполнения (а не во время компиляции), если модуль должен задействовать
значение, определенное в другом модуле. В этом случае вместо констант следует
использовать предназначенные только для чтения поля, о которых речь идет
в следующем разделе.
Do'stlaringiz bilan baham: |