Статическое чтение только против const


1387

Я прочитал о constи static readonlyполей. У нас есть несколько классов, которые содержат только постоянные значения. Используется для различных вещей в нашей системе. Так что мне интересно, если мои наблюдения верны:

Должны ли такого рода постоянные значения всегда быть static readonlyдля всего, что является публичным? И использовать только constдля внутренних / защищенных / частных ценностей?

Что вы порекомендуете? Должен ли я даже не использовать static readonlyполя, а использовать свойства, может быть?


5
Вот один очень интересный случай, который я только что нашел в пользу static readonly: попробуйте использовать const внутри, IEnumeratorкоторый вызовет необратимое, yield и вы получите страшную «Внутреннюю ошибку компилятора» . Я не тестировал код вне Unity3D, но я уверен, что это ошибка в моно или .NET . Это C # вопрос , тем не менее.
Cregox

2
Возможный дубликат В чем разница между const и readonly?
Nawfal

8
Другое отличие состоит в том, что вы можете использовать const-строку в коммутаторе, но не статическую строку только для
чтения

7
static readonlyне может использоваться в switch-caseвыражении как caseпеременная, constдля этого требуется.
Мостафиз Рахман

3
static readonlyтакже нельзя использовать в качестве параметра атрибута
Dread Boy

Ответы:


940

public static readonlyполя немного необычны; public staticсвойства (только с a get) будут более распространенными (возможно, подкрепленными private static readonlyполем).

constзначения записываются непосредственно в колл-сайт; это обоюдоострый:

  • бесполезно, если значение выбирается во время выполнения, возможно, из конфигурации
  • если вы измените значение const, вам нужно перестроить все клиенты
  • но это может быть быстрее, так как избегает вызова метода ...
  • ... которые иногда могут быть вставлены JIT в любом случае

Если значение никогда не изменится, тогда const в порядке - и Zeroт. Д. Делают разумные const; p Кроме того, staticсвойства более распространены.


13
Почему собственность над полем? Если это неизменный класс, я не вижу разницы.
Майкл Хедгпет

73
@ Майкл - те же причины, что и всегда; это скрывает реализацию. Вы можете (позже) обнаружить, что вам нужно загружаться лениво, на основе конфигурации, фасада или чего-то еще. На самом деле, с любым из них все будет в порядке ...
Марк Гравелл

42
@CoffeeAddict по определению, константа не извлекает значения из файла конфигурации; он записывается как литерал во время компиляции. Единственный способ использовать константу во время выполнения - это отражение над полями. В любое другое время, когда вы пытаетесь его использовать, компилятор уже подставил ваше постоянное использование для буквального использования; т. е. если метод в вашем коде использует 6 констант, а вы проверяете его как IL, не будет упоминаний о каких-либо постоянных поисках; буквальные значения будут просто загружены на месте
Марк Гравелл

37
@MarcGravell - ВНИМАНИЕ: readonlyполя не могут использоваться в выражениях switch / case, вместо этого вам нужно, чтобы они были const.
Лучано

7
@didibus Изменение поля на свойство фактически нарушает API. Поле в C # эффективно действует как переменная, в то время как свойство в C # является синтаксическим помощником для написания метода получения и / или метода установки. Эта разница важна, когда участвуют другие сборки. Если вы изменили поле на свойство, и другие сборки зависели от этого поля, то эти другие сборки необходимо перекомпилировать.
Стивен Бухер

237

Я бы использовал, static readonlyесли Потребитель находится в другой сборке. Наличие constи Потребителя в двух разных сборках - хороший способ выстрелить себе в ногу .


5
Поэтому я думаю, что, как некоторые упоминали или упоминали, может быть целесообразно использовать const только для значений, которые на самом деле являются хорошо известными константами, если они обнародованы, в противном случае они должны быть зарезервированы для внутренней, защищенной или закрытой области доступа.
jpierson

1
@Dio Причина, по которой он все еще существует, заключается в том, что он сам по себе не является проблемой - это что-то, о чем нужно знать, но возможность встроить концы через границы сборки - хорошая вещь для производительности. Это просто вопрос понимания того, что «константа» означает «это никогда не изменится».
Майкл Стум

1
@MichaelStum Хорошо, я не должен называть это "проблемой". В своей работе я использую const и разделяю его между сборками, но перекомпилирую для каждого развертывания или отправки кода. Тем не менее, этот факт определенно стоит принять к сведению.
Дио Пхунг

1
Так что, в общем, internal constили в public static readonlyзависимости от желаемой видимости.
Ииридайн

2
@Iiridayn Да, это не плохой взгляд на это. Есть несколько крайних случаев, которые нужно рассмотреть (например, если используется Reflection или если для атрибута требуется значение), и есть допустимые варианты использования public const(например, что-нибудь в стандарте. Каждый раз, когда я работаю с XML, есть Файл пространств имен с кучей public const string.) Но в целом, public constследует использовать только после правильного рассмотрения последствий.
Майкл Стум

200

Еще несколько важных вещей, которые следует отметить:

const int a

  • должен быть инициализирован.
  • инициализация должна быть во время компиляции .

только для чтения в

  • можно использовать значение по умолчанию, без инициализации.
  • инициализация может быть выполнена во время выполнения (Edit: только внутри конструктора).

39
в пределах ctorединственного.
Амит Кумар Гош

1
Не только в конструкторе, но и в объявлении ( docs.microsoft.com/en-us/dotnet/csharp/language-reference/… ).
deChristo

176

Это просто дополнение к другим ответам. Я не буду их повторять (сейчас четыре года спустя).

Есть ситуации, когда a constи неконстантный имеют разную семантику. Например:

const int y = 42;

static void Main()
{
  short x = 42;
  Console.WriteLine(x.Equals(y));
}

распечатывает True, тогда как:

static readonly int y = 42;

static void Main()
{
  short x = 42;
  Console.WriteLine(x.Equals(y));
}

пишет False.

Причина в том, что метод x.Equalsимеет две перегрузки, одна из которых принимает short( System.Int16), а другая - object( System.Object). Теперь вопрос в том, применим ли один или оба к моему yаргументу.

Когда yэто константа времени компиляции (литерал), constстановится важным, чтобы существовало неявное преобразование из int в, short при условии, что intэто константа, и при условии, что компилятор C # проверяет, что ее значение находится в пределах диапазона short( который 42есть). См. Неявные преобразования константных выражений в Спецификации языка C #. Таким образом, обе перегрузки должны быть учтены. Перегрузка Equals(short)предпочтительна (любая shortесть object, но не все objectесть short). Так yчто преобразуется в short, и эта перегрузка используется. Затем Equalsсравнивает два shortодинаковых значения, и это дает true.

Когда yне является константой, неявное преобразование из intв не shortсуществует. Это потому, что в общем случае an intможет быть слишком большим, чтобы помещаться в a short. ( Явное преобразование существует, но я не сказал Equals((short)y), так что это не имеет значения.) Мы видим, что применяется только одна перегрузка - Equals(object)одна. Так yв штучной упаковке object. Затем Equalsбудет сравнивать a System.Int16с a System.Int32, и поскольку типы времени выполнения даже не согласуются, это даст результат false.

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


17
Хорошее дополнение к принятому ответу. Я хотел бы добавить, что правильное преобразование типов данных и другие подобные рекомендации (такие как try catch и т. Д.) Должны быть одним из основных продуктов опытных программистов, а не предоставляться компилятору. Тем не менее, я узнал что-то новое отсюда. Спасибо.
Uknight

Вау, я давно программирую на C # и никогда бы не подумал, что const int в диапазоне short может быть неявно преобразован в short. Я должен сказать, что это довольно странно. Я люблю C #, но эти странные несоответствия, которые, кажется, не добавляют большой ценности, но добавляют много необходимой мозговой силы, чтобы постоянно их рассматривать, могут раздражать, особенно для начинающих.
Майк Мариновски,

@MikeMarynowski Достаточно верно. Но я думаю, что они сделали это правило (среди прочих причин), чтобы сделать заявление short x = 42;законным. Потому что там у вас есть int, а именно, литерал 42, который неявно превращается в short x. Но тогда они могли бы ограничить это только числовыми литералами; однако, они решили также разрешить такие вещи, как, short x = y;где yопределяется const int y = 42;, и затем они закончили с этим.
Джеппе Стиг Нильсен

88

Следует отметить, что const ограничен типами примитивов / значений (исключение составляют строки)


30
На самом деле constможет использоваться и для других типов, за исключением того, что он должен быть инициализирован нулем, что делает его бесполезным :)
nawfal

6
исключение как в System.Exception? :)
Memet Olsen

4
@nawfal Более точно, только типы значений , для которых constмогут быть использованы, являются sbyte, byte, short, ushort, int, uint, long, ulong, char, float, double, decimal, bool, плюс любые enumтипы. constне может использоваться для других типов значений, таких как DateTimeили TimeSpanили BigInteger. Он также не может использоваться для IntPtrструктуры (некоторые считают ее «примитивным»; термин «примитивный тип» в C # сбивает с толку). ↵↵ constМожет использоваться для всех типов ссылок . Если тип имеет stringзначение, можно указать любое строковое значение. В противном случае значение должно быть null.
Джепп Стиг Нильсен

@JeppeStigNielsen - Я недавно был спор с servy об этом - он указал на то , что вы можете сделать что - нибудь (значение и ссылочные типы) , constиспользуя default. Для structтипов это экземпляр со всеми его членами, установленными в значения по умолчанию.
Вай Ха Ли

28

Только для чтения : значение может быть изменено через staticконструктор во время выполнения. Но не через функцию члена.

Постоянная : по умолчанию static. Значение не может быть изменено из любого места (Ctor, Function, время выполнения и т. Д. Не где).

Только чтение : значение может быть изменено через конструктор во время выполнения. Но не через функцию члена.

Вы можете взглянуть на мои свойства репо: C # .


1
Плохие новости ... битая ссылка!
Fer R


Хорошие фрагменты Сиам ভাই :)
Мухаммед Ашикуззаман

25

readonlyКлючевое слово отличается от constключевого слова. constПоле может быть инициализированы только при объявлении поля. readonlyПоле может быть инициализирован либо при объявлении или в конструкторе. Следовательно, readonlyполя могут иметь разные значения в зависимости от используемого конструктора. Кроме того, хотя constполе является константой времени компиляции, readonlyполе может использоваться для констант времени выполнения

Краткая и понятная ссылка на MSDN здесь


16

constи readonlyпохожи, но они не совсем одинаковы.

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

Например, constчлены могут использоваться для определения таких членов, как:

struct Test
{
    public const double Pi = 3.14;
    public const int Zero = 0;
}

Так как значения как 3.14 и 0 являются константами времени компиляции. Тем не менее, рассмотрим случай, когда вы определяете тип и хотите предоставить некоторые заранее созданные экземпляры этого типа. Например, вы можете определить класс Color и предоставить «константы» для общих цветов, таких как черный, белый и т. Д. Это невозможно сделать с помощью константных членов, поскольку правые части не являются константами времени компиляции. Это можно сделать с помощью обычных статических членов:

public class Color
{
    public static Color Black = new Color(0, 0, 0);
    public static Color White = new Color(255, 255, 255);
    public static Color Red   = new Color(255, 0, 0);
    public static Color Green = new Color(0, 255, 0);
    public static Color Blue  = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}

Но тогда ничто не может помешать клиенту Color изменить его, возможно, путем замены значений Black и White. Излишне говорить, что это вызвало бы смятение у других клиентов класса Color. Функция «только для чтения» предназначена для этого сценария.

Просто вводя readonlyключевое слово в объявлениях, мы сохраняем гибкую инициализацию, в то же время предотвращая перехват кода клиента.

public class Color
{
    public static readonly Color Black = new Color(0, 0, 0);
    public static readonly Color White = new Color(255, 255, 255);
    public static readonly Color Red   = new Color(255, 0, 0);
    public static readonly Color Green = new Color(0, 255, 0);
    public static readonly Color Blue  = new Color(0, 0, 255);
    private byte red, green, blue;

    public Color(byte r, byte g, byte b) => (red, green, blue) = (r, g, b);
}

Интересно отметить, что члены-константы всегда являются статическими, тогда как член только для чтения может быть статическим или нет, как обычное поле.

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

public class A
{
    public static const C = 0;
}

и другой разработчик написал код, который опирался на A:

public class B
{
    static void Main() => Console.WriteLine(A.C);
}

Теперь, может ли генерируемый код опираться на тот факт, что AC является константой времени компиляции? Т.е. можно ли просто заменить использование AC значением 0? Если вы скажете «да» на это, то это означает, что разработчик A не может изменить способ инициализации AC - это связывает руки разработчика A без разрешения.

Если вы ответите «нет» на этот вопрос, то пропустите важную оптимизацию. Возможно, автор A уверен, что AC всегда будет нулевым. Использование const и readonly позволяет разработчику A указать намерение. Это улучшает поведение при управлении версиями, а также повышает производительность.


12

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

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


7

Const: Const - это не что иное, как «константа», переменная, значение которой является постоянным, но во время компиляции. И обязательно присвоить ему значение. По умолчанию const является статическим, и мы не можем изменить значение переменной const во всей программе.

Static ReadOnly: значение переменной типа Static Readonly может быть назначено во время выполнения или назначено во время компиляции и изменено во время выполнения. Но значение этой переменной можно изменить только в статическом конструкторе. И не может быть изменено в дальнейшем. Это может измениться только один раз во время выполнения

Ссылка: c-sharpcorner


6

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

Например, предположим, что сборка Xпредоставляет константу следующим образом:

public const decimal ProgramVersion = 2.3;

Если сборка Yссылается на Xэту константу и использует ее, значение 2.3 будет встроено в сборку Yпри компиляции. Это означает, что если Xпозже будет перекомпилирован с константой, установленной в 2.4, Yвсе равно будет использоваться старое значение 2.3, пока не Yбудет перекомпилировано. Статическое поле только для чтения позволяет избежать этой проблемы.

Другой способ взглянуть на это состоит в том, что любое значение, которое может измениться в будущем, не является постоянным по определению и поэтому не должно быть представлено как единое целое.


3

Const:

  1. значение должно быть указано при объявлении
  2. постоянная времени компиляции

только для чтения:

  1. значение может быть дано при объявлении или во время выполнения с использованием конструкторов. Значение может варьироваться в зависимости от используемого конструктора.
  2. постоянная времени выполнения

3

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

ReadOnly : значения переменных только для чтения, которые мы можем определить при объявлении, а также при использовании конструктора во время выполнения. Переменные только для чтения не могут получить доступ без экземпляра класса.

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

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

https://www.stum.de/2009/01/14/const-strings-a-very-convenient-way-to-shoot-yourself-in-the-foot/


Не могли бы вы, пожалуйста, скажите мне, почему вы отказались от ответа, чтобы я мог обновить себя, а также здесь.
user1756922

Не DV, но, возможно, этот ответ ничего не добавляет к уже исчерпывающим ответам здесь.
Марк Л.

действительно, помните, что в Java в конце 90-х у нас в проекте было несколько человек, производящих разные jar с файлами классов, которые взаимодействовали (ссылались друг на друга) и общедоступная строка const имела проблемы с версионированием, поскольку они копировались вокруг
Джордж Бирбилис

2

Существует небольшая разница между полями const и static readonly в C # .Net

const должен быть инициализирован значением во время компиляции.

const по умолчанию является статическим и должен быть инициализирован постоянным значением, которое нельзя изменить позже. Его нельзя использовать со всеми типами данных. Для экс-DateTime. Его нельзя использовать с типом данных DateTime.

public const DateTime dt = DateTime.Today;  //throws compilation error
public const string Name = string.Empty;    //throws compilation error
public static readonly string Name = string.Empty; //No error, legal

readonly может быть объявлен как статический, но не обязательный. Не нужно инициализировать во время объявления. Его значение может быть присвоено или изменено с помощью конструктора один раз. Таким образом, существует возможность один раз изменить значение поля только для чтения (не имеет значения, статическое оно или нет), что невозможно с помощью const.


0

Константы, как следует из названия, поля, которые не меняются и обычно определяются статически во время компиляции в коде.

Переменные только для чтения - это поля, которые могут изменяться при определенных условиях.

Они могут быть инициализированы при первом объявлении их как константы, но обычно они инициализируются во время конструирования объекта внутри конструктора.

Они не могут быть изменены после инициализации в условиях, указанных выше.

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

Также еще одно важное отличие состоит в том, что константа принадлежит классу, а переменная только для чтения принадлежит экземпляру!


0

Const (определяемый во время компиляции) может быть использован в тех случаях, когда статический readonly не может, например, в операторах switch или конструкторах атрибутов. Это связано с тем, что поля только для чтения разрешаются только во время выполнения, а для некоторых конструкций кода требуется гарантия времени компиляции. Статическая информация только для чтения может быть рассчитана в конструкторе, что часто является важной и полезной вещью. Разница функциональная, как и должно быть их использование на мой взгляд.

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

Лично у меня по умолчанию статический режим только для чтения, так как он имеет для меня более смысловой и логический смысл, тем более что большинство значений не нужны во время компиляции. И, между прочим, общедоступная статическая информация только для чтения не является чем-то необычным или необычным, как отмечается отмеченным ответом: например, System.String.Emptyодин.


0

Другое различие между объявлением const и static readonly заключается в распределении памяти.

Статическое поле принадлежит типу объекта, а не экземпляру этого типа. В результате, как только на класс ссылаются в первый раз, статическое поле будет «жить» в памяти в течение остального времени, и все экземпляры этого типа будут ссылаться на один и тот же экземпляр статического поля.

С другой стороны, поле const "принадлежит экземпляру типа.

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

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.