Кажется, существует три варианта для реализации публично доступных констант в C#. Мне любопытно, если существуют какие-либо серьезные основания выбрать один по другой или если это - просто вопрос персонального предпочтения.
Выбор 1 - частное поле плюс метод считывания свойства
private const string _someConstant = "string that will never change";
public string SomeConstant
{
get { return _someConstant; }
}
Выбор 2 - метод считывания свойства только
public string SomeConstant
{
get { return "string that will never change"; }
}
Выбор 3 - общедоступное поле только
public const string SomeConstant = "string that will never change";
Который Вы рекомендуете и почему?
Обновление
По-видимому, это превратилось в обсуждение того, использовать ли const
или static readonly
. Не точно, что я предназначил, но это действительно учило меня, что Выбором 3 является определенно плохая идея, потому что, если значение константы изменяется в будущей версии, это требует, чтобы все блоки ссылки были перекомпилированы.
Однако я не думаю, что любой действительно обсудил Выбор 2 все же. Мне все еще любопытно, если существует какой-либо недостаток с наличием только что метода считывания, который возвращает значение и ничто иное.
Варианты 1 и 2 эквивалентны, на самом деле.
Мне кажется, есть три разные ситуации:
Вы точно знаете, что строка никогда, никогда не изменится. В этом случае разумно сделать ее const
. (Например, Math.PI
- const. В ближайшее время это не изменится.) Есть некоторые тонкие последствия для памяти, связанные с использованием static readonly
, но они вряд ли повлияют на вас. Вы не должны делать этого, если значение может измениться и вы не хотите перекомпилировать всех вызывающих в этой ситуации, по причинам, указанным в другом месте. Обратите внимание, что для многих проектов (особенно внутрикорпоративных) перекомпиляция всех вызывающих команд не является проблемой.
Вы думаете, что строка может измениться в будущем, но знаете, что она всегда будет константой в пределах одной версии. В этом случае подойдет поле public static readonly
. Имейте в виду, что это можно делать со строками, поскольку они неизменяемы, но не следует делать это с любыми изменяемыми типами, такими как массивы. (Либо создавайте неизменяемые коллекции, либо используйте свойство и возвращайте каждый раз новую копию).
Вы думаете, что строка может измениться, и она может измениться даже в течение жизни программы... например, "текущая дата, отформатированная". В этом случае используйте public static read-only property (свойство, имеющее только getter). Обратите внимание, что переход от поля только для чтения к свойству только для чтения является source-совместимым изменением, но не binary-совместимым изменением - так что если вы остановились на моем втором варианте, но затем вам нужно перейти на третий, вам нужно все перекомпилировать.
Если бы я мог проголосовать - я бы проголосовал за ответ Джона Скита. Чтобы добавить к этому, ваши №1 и №2 в точности идентичны, как показано здесь в IL:
.method public hidebysig specialname instance string
get_SomeConstant() cil managed
{
// Code size 11 (0xb)
.maxstack 1
.locals init ([0] string CS$1$0000)
IL_0000: nop
IL_0001: ldstr "string that will never change"
IL_0006: stloc.0
IL_0007: br.s IL_0009
IL_0009: ldloc.0
IL_000a: ret
} // end of method Class1::get_SomeConstant
Вариант №2:
.method public hidebysig specialname instance string
get_SomeConstant() cil managed
{
// Code size 11 (0xb)
.maxstack 1
.locals init ([0] string CS$1$0000)
IL_0000: nop
IL_0001: ldstr "string that will never change"
IL_0006: stloc.0
IL_0007: br.s IL_0009
IL_0009: ldloc.0
IL_000a: ret
} // end of method Class2::get_SomeConstant
Теперь рассмотрим вариант №3. Номер 3 сильно отличается от №1 и №2. Причина этого в том, что, как указывалось ранее, № 3 статичен, поскольку const статичен. Теперь реальный вопрос заключается в том, чтобы сравнить яблоки с яблоками в том, что, если №1 и №2, где статические аксессоры? Тогда они были бы более сопоставимы с №3. В настоящее время вам необходимо инициализировать класс для вариантов 1 и 2, но не для № 3. Таким образом, в этом случае происходит ненужная инициализация объекта, и вы всегда хотите использовать static, когда это возможно, чтобы избежать этого.
Теперь давайте посмотрим на номер 3 в IL:
.field public static literal string SomeConstant = "string that will never change"
Итак, для эффективности я бы использовал номер 3. Этому же меня учили многие талантливые коллеги на протяжении многих лет.
Теперь обратимся к белому слону в комнате. Readonly и const отличаются тем, что cont происходит во время компиляции, а readonly происходит во время выполнения. Статический только для чтения инициализируется один раз, а нестатический только для чтения инициализируется один раз для каждого экземпляра. Если, например, вы задаете свой вопрос, чтобы сделать что-то вроде создания класса константных строк для сообщений об ошибках, которые никогда не изменятся, используйте вариант № 3, а не только для чтения, статический или иначе. Подумайте о попытке инициализировать сотни сообщений об ошибках во время выполнения, а не во время компиляции, вы увидите заметную разницу в производительности. Кроме того, поскольку вы четко заявляете, что это «строка, которая никогда не изменится», только чтение не следует даже рассматривать в этом случае, потому что «... она никогда не изменится».Const и ReadOnly имеют свои места, но readonly не предназначен для элементов, которые никогда не изменятся и известны во время компиляции.
Члены const
являются членами класса, а не членами экземпляра (другими словами, const
подразумевает static
).
Рассмотрим
public static readonly string myVar = "something";
Причина: когда вы предоставляете (а затем потребляете в другом месте) const
, const
внедряется в метаданные потребляющего типа.
public static readonly
— нет, и поскольку это static readonly
, он стоит экземпляра только один раз, и он неизменяем, как const
.
Правильным является выбор #4:
public static readonly string SomeConstant = "string that might change in a new version";
Важно использовать поле readonly вместо публичного const. Литеральное значение const компилируется в IL. Если изменить значение const и перекомпилировать одну сборку, которая его использует, у вас будет несоответствие с другими сборками, которые также используют const. Эти другие сборки по-прежнему будут выполняться со старым значением const. Очень трудно диагностировать.
Это не может произойти с полем, только для чтения, другие сборки всегда будут считывать обновленное значение. Если вы используете const, обязательно всегда делайте их частными.
Свойство действительно кажется лучшим выбором, потому что возвращаемая строка
не встраивается в метаданные. const
действительно предназначены для внутреннего использования (например, номер версии может быть сделан в const
и может измениться) или для значений, которые абсолютно никогда не изменятся, насколько это касается кода, который ссылается на них.