Как лучше всего реализовать публично доступные константы в C#

Кажется, существует три варианта для реализации публично доступных констант в 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 все же. Мне все еще любопытно, если существует какой-либо недостаток с наличием только что метода считывания, который возвращает значение и ничто иное.

8
задан devuxer 26 February 2015 в 23:17
поделиться

6 ответов

Варианты 1 и 2 эквивалентны, на самом деле.

Мне кажется, есть три разные ситуации:

  • Вы точно знаете, что строка никогда, никогда не изменится. В этом случае разумно сделать ее const. (Например, Math.PI - const. В ближайшее время это не изменится.) Есть некоторые тонкие последствия для памяти, связанные с использованием static readonly, но они вряд ли повлияют на вас. Вы не должны делать этого, если значение может измениться и вы не хотите перекомпилировать всех вызывающих в этой ситуации, по причинам, указанным в другом месте. Обратите внимание, что для многих проектов (особенно внутрикорпоративных) перекомпиляция всех вызывающих команд не является проблемой.

  • Вы думаете, что строка может измениться в будущем, но знаете, что она всегда будет константой в пределах одной версии. В этом случае подойдет поле public static readonly. Имейте в виду, что это можно делать со строками, поскольку они неизменяемы, но не следует делать это с любыми изменяемыми типами, такими как массивы. (Либо создавайте неизменяемые коллекции, либо используйте свойство и возвращайте каждый раз новую копию).

  • Вы думаете, что строка может измениться, и она может измениться даже в течение жизни программы... например, "текущая дата, отформатированная". В этом случае используйте public static read-only property (свойство, имеющее только getter). Обратите внимание, что переход от поля только для чтения к свойству только для чтения является source-совместимым изменением, но не binary-совместимым изменением - так что если вы остановились на моем втором варианте, но затем вам нужно перейти на третий, вам нужно все перекомпилировать.

10
ответ дан 5 December 2019 в 07:57
поделиться

Если бы я мог проголосовать - я бы проголосовал за ответ Джона Скита. Чтобы добавить к этому, ваши №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 не предназначен для элементов, которые никогда не изменятся и известны во время компиляции.

1
ответ дан 5 December 2019 в 07:57
поделиться

Члены const являются членами класса, а не членами экземпляра (другими словами, const подразумевает static ).

2
ответ дан 5 December 2019 в 07:57
поделиться

Рассмотрим

public static readonly string myVar = "something";

Причина: когда вы предоставляете (а затем потребляете в другом месте) const, const внедряется в метаданные потребляющего типа.

public static readonly — нет, и поскольку это static readonly, он стоит экземпляра только один раз, и он неизменяем, как const.

5
ответ дан 5 December 2019 в 07:57
поделиться

Правильным является выбор #4:

 public static readonly string SomeConstant = "string that might change in a new version";

Важно использовать поле readonly вместо публичного const. Литеральное значение const компилируется в IL. Если изменить значение const и перекомпилировать одну сборку, которая его использует, у вас будет несоответствие с другими сборками, которые также используют const. Эти другие сборки по-прежнему будут выполняться со старым значением const. Очень трудно диагностировать.

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

2
ответ дан 5 December 2019 в 07:57
поделиться

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

1
ответ дан 5 December 2019 в 07:57
поделиться
Другие вопросы по тегам:

Похожие вопросы: