Как лучше всего реализовать общедоступные константы в 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 определенно плохая идея, потому что, если значение const изменится в будущей версии, потребуется перекомпиляция всех ссылочных сборок.
Тем не менее, я не думаю, что кто-то действительно обсуждал Вариант 2. Мне все еще интересно, есть ли какой-то недостаток в том, чтобы иметь только метод получения, который возвращает значение, и ничего больше.
6 ответов
Варианты 1 и 2 на самом деле эквивалентны.
Мне кажется, что на самом деле есть три разные ситуации:
Вы точно знаете, что строка никогда не изменится. В этом случае разумно сделать это
const
, (Например,Math.PI
является постоянным Это не изменится в ближайшее время.) Есть некоторые тонкие последствия для памяти при использовании этого по сравнению с использованиемstatic readonly
, но они вряд ли повлияют на вас. Вы не должны делать этого, если значение может измениться, и вы не хотите перекомпилировать всех вызывающих в этой ситуации по причинам, указанным в другом месте. Обратите внимание, что для многих проектов (особенно внутрикорпоративных) на самом деле не проблема перекомпилировать всех вызывающих.Вы думаете, что строка может измениться в будущем, но вы знаете, что она всегда будет постоянной в любой версии. В этом случае
public static readonly
поле в порядке. Имейте в виду, что это хорошо делать со строками, поскольку они неизменяемы, но вы не должны делать это с любыми изменяемыми типами, такими как массивы. (Либо предоставьте неизменные коллекции, либо используйте свойство и каждый раз возвращайте новую копию.)Вы думаете, что строка может измениться, и она может даже измениться за время существования программы... например, "текущая дата, отформатированная". В этом случае используйте открытое статическое свойство только для чтения (одно только с геттером). Обратите внимание, что изменение поля "только для чтения" на свойство "только для чтения" является изменением, совместимым с исходным кодом, но не изменением, совместимым с двоичным кодом, поэтому, если вы набрали второй пункт, но затем должны перейти к третьему, вам нужно перекомпилировать все.
Рассматривать
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";
Важно использовать поле только для чтения вместо открытого const. Буквальное значение const компилируется в IL. Если вы измените значение const и перекомпилируете одну сборку, которая его использует, теперь у вас будет несоответствие с другими сборками, которые также используют const. Эти другие сборки будут по-прежнему работать со старым значением const. Очень сложно диагностировать.
Это не может произойти с полем только для чтения, другие сборки всегда будут читать обновленное значение. Если вы используете const, всегда делайте их приватными.
const
члены являются членами класса, а не членами экземпляра (другими словами, const
подразумевает static
).
Недвижимость действительно кажется лучшим выбором, потому что возвращенный string
затем не включается в метаданные. const
действительно предназначены для внутреннего использования (например, номер версии может быть превращен в const
и может измениться) или для значений, которые абсолютно никогда не изменятся, насколько это касается кода, ссылающегося на него.
Если бы я мог проголосовать - я бы проголосовал за ответ Джона Скита. Чтобы добавить к этому, ваши #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. Таким образом, в этом случае происходит ненужная инициализация объекта, и вы всегда хотите использовать статический по возможности, чтобы избежать этого.
Теперь давайте посмотрим на номер 3 в IL:
.field public static literal string SomeConstant = "string that will never change"
Поэтому для эффективности я бы использовал № 3. Это то, чему меня учили многие талантливые сверстники на протяжении многих лет.
Теперь обратимся к белому слону в комнате. Readonly и const отличаются тем, что cont происходят во время компиляции и readonly происходит во время выполнения. Статическое чтение только инициализируется один раз, в то время как нестатическое чтение только инициализируется один раз за экземпляр. Если, например, вы задаете свой вопрос, например, создайте класс константных строк для сообщений об ошибках, которые никогда не изменятся, тогда используйте параметр № 3, а не только для чтения, статический или другой. Подумайте о попытке инициализации сотен сообщений об ошибках во время выполнения, а не во время компиляции, вы увидите заметную разницу в производительности. Кроме того, поскольку вы четко заявляете, что это "строка, которая никогда не изменится", только для чтения не следует даже рассматривать в этом случае, потому что "... она никогда не изменится". Const и ReadOnly имеют свои места, но readonly не для элементов, которые никогда не изменятся и известны во время компиляции.