Почему String.Empty не является константой?

189

В .Net почему String.Empty только для чтения вместо константы? Мне просто интересно, знает ли кто-нибудь, в чем причина этого решения.

Трэвис
источник
5
Этот вопрос может решить этот вопрос, краткий ответ, никто не знает ...
Гдорон поддерживает Монику
Да, +1 за ответ Эрика Липперта, спасибо!
Трэвис
Особенно учитывая, что Decimal.Zero является константой (с точки зрения пользователя, то есть ...)
Хэмиш Грубиджан

Ответы:

149

Причина, по static readonlyкоторой вместо этого constпроисходит, связана с использованием неуправляемого кода, как указано Microsoft здесь, в общеязыковой версии 2.0 Shared Source . Файл для просмотра есть sscli20\clr\src\bcl\system\string.cs.

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

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

Я нашел эту информацию из этой удобной статьи в CodeProject .

Джефф Йейтс
источник
Я был бы очень признателен, если бы вы могли объяснить этот комментарий (потому что Джон Скит не смог ...), см. Здесь: stackoverflow.com/questions/8462697/…
gdoron поддерживает Монику
2
@gdoron: Мое предположение (и это предположение) заключается в следующем. Когда значение определено как литерал (константа), его значение вставляется в места, где на него ссылаются, тогда как когда оно не определено как литерал, ссылка на источник значения указывается, и фактическое значение извлекается во время выполнения. Я подозреваю, что последний может обеспечить правильное распределение строк между собственным и .NET во время выполнения - если бы он был литералом, возможно, нативный компилятор должен был бы каким-то образом извлечь значение литерала в свой собственный код, который, вероятно, не возможно. Это все гипотеза с моей стороны, хотя.
Джефф Йейтс
7
Это означает, что нужно использовать "", а не string.Empty для значений параметров по умолчанию в методах. Что немного раздражает.
nicodemus13
17
«» может выглядеть как ошибка, в то время как string.Empty показывает намеренное намерение
Кристофер Стивенсон
3
@JeffYates Я бы добавил, что тот факт, что это не соответствует, уже раздражает. Люди увидят оставшуюся часть кода и зададутся вопросом «почему он использует» здесь вместо String.Empty? Я серьезно подумываю не использовать String.Emptyбольше по этой причине в одиночку.
Jullealgon
24

Я думаю, что здесь много путаницы и плохих ответов.

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

Проверьте раздел 10.4 Константы спецификации языка C #.

Даже если константы считаются статическими членами, объявление константы не требует и не допускает статический модификатор.

Если public const члены являются статическими, нельзя считать, что константа создаст новый объект.

Учитывая это, следующие строки кода делают абсолютно то же самое в отношении создания нового объекта.

public static readonly string Empty = "";
public const string Empty = "";

Вот примечание от Microsoft, которое объясняет разницу между двумя:

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

Поэтому я считаю, что единственный правдоподобный ответ здесь - это Джефф Йейтс.

Бруно Конде
источник
+1 за добрые слова и разъяснения относительно спецификации C # на const и static readonly.
Джефф Йейтс
17
Перечитывая это, я не согласен с этим const stringи static readonly stringделаю то же самое. Const-значения подставляются в связанный код, тогда как статические значения доступны только для чтения. Если у вас есть constбиблиотека A, которая используется библиотекой B, библиотека B заменит все ссылки на эту constпеременную ее литеральным значением; если бы эта переменная была static readonlyвзамен, на нее будет ссылаться и ее значение будет определено во время выполнения.
Джефф Йейтс
3
Точка зрения Джеффа важна при обращении к библиотекам. Если вы перекомпилируете A и перераспределите его, без перекомпиляции B , B все равно будет использовать старые значения.
Марк Соул
5
String.Empty read only instead of a constant?

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

Если вы оставите вашу строку только для чтения в одном месте, как она есть String.Empty, программа сохранит одну и ту же строку только в одном месте и прочитает ее или обратится к ней - сохраняя минимум данных в памяти.

Также, если вы скомпилируете любую dll, используя String.Empty в качестве const, и по какой-либо причине измените String.Empty, то скомпилированная dll больше не будет работать так же, потому costчто внутренний код создает фактическую копию строки на каждый звонок.

Посмотрите этот код, например:

public class OneName
{
    const string cConst = "constant string";
    static string cStatic = "static string";
    readonly string cReadOnly = "read only string";

    protected void Fun()
    {
        string cAddThemAll ;

        cAddThemAll = cConst;
        cAddThemAll = cStatic ;
        cAddThemAll = cReadOnly;    
    }
}

будет приходить компилятор как:

public class OneName
{
    // note that the const exist also here !
    private const string cConst = "constant string";
    private readonly string cReadOnly;
    private static string cStatic;

    static OneName()
    {
        cStatic = "static string";
    }

    public OneName()
    {
        this.cReadOnly = "read only string";
    }

    protected void Fun()
    {
        string cAddThemAll ;

        // look here, will replace the const string everywhere is finds it.
        cAddThemAll = "constant string";
        cAddThemAll = cStatic;
        // but the read only will only get it from "one place".
        cAddThemAll = this.cReadOnly;

    }
}

и сборка вызова

        cAddThemAll = cConst;
0000003e  mov         eax,dword ptr ds:[09379C0Ch] 
00000044  mov         dword ptr [ebp-44h],eax 
        cAddThemAll = cStatic ;
00000047  mov         eax,dword ptr ds:[094E8C44h] 
0000004c  mov         dword ptr [ebp-44h],eax 
        cAddThemAll = cReadOnly;
0000004f  mov         eax,dword ptr [ebp-3Ch] 
00000052  mov         eax,dword ptr [eax+0000017Ch] 
00000058  mov         dword ptr [ebp-44h],eax 

Редактировать: Исправлена ​​опечатка

Аристос
источник
Итак, это означает, что строка const должна всегда создаваться с помощью класса, содержащего этот const? Кажется, тогда лучше использовать статическое чтение только для чтения.
Берсеркер
@theberserker лучше, но у вас есть все возможности для использования.
Аристос
> тогда скомпилированная dll больше не будет работать так же, потому что затраты на создание внутреннего кода фактически сохраняют копию строки при каждом вызове. @ Аристос: Это не совсем верно. Как только код скомпилирован, на «копию» строки будет ссылаться в блоке TEXT исполняемого файла, и весь код будет просто ссылаться на этот же блок памяти. То, что вы упомянули на втором этапе, является просто промежуточным этапом.
Питер Долкенс,
@ user1533523 спасибо за примечание - я проведу тест, когда найду время, чтобы проверить это
Аристос
Как вы получили этот ассемблерный код? C # не компилируется в сборку!
jv110
0

Этот ответ существует в исторических целях.

Первоначально:

Потому Stringчто это класс и, следовательно, не может быть константой.

Расширенное обсуждение:

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

В .NET (в отличие от Java) строка и строка в точности совпадают. И да, вы можете иметь строковые литеральные константы в .NET - DrJokepu 3 февраля 2009 г. в 16:57

Вы говорите, что у класса не может быть констант? - StingyJack 3 февраля 2009 в 16:58

Да, объекты должны использовать только для чтения. Только структуры могут делать константы. Я думаю, что когда вы используете stringвместо Stringкомпилятора, const превращается в readonly для вас. Все, что нужно сделать, чтобы программисты на C были довольны. - Гарри Шатлер 3 февраля 2009 года в 16:59

Тванфоссон только что объяснил это более многословно. «X не может быть константой, потому что содержащий Y является классом» было слишком мало контекстно-зависимым;) - Leonidas 3 февраля 2009 г. в 17:01

string.Empty - это статическое свойство, которое возвращает экземпляр класса String, а именно пустую строку, а не сам класс строки. - tvanfosson 3 февраля 2009 г. в 17:01

Empty - это доступный только для чтения экземпляр (это не свойство) класса String. - Senfo 3 февраля 2009 в 17:02

Голова болит. Я все еще думаю, что я прав, но теперь я менее уверен. Исследование требуется сегодня вечером! - Гарри Шатлер 3 февраля 2009 года в 17:07

Пустая строка является экземпляром класса string. Пустое - это статическое поле (я не исправляю это свойство) в классе String. В основном разница между указателем и тем, на что он указывает. Если бы это было не только для чтения, мы могли бы изменить, к какому экземпляру относится пустое поле. - tvanfosson 3 февраля 2009 г. в 17:07

Гарри, тебе не нужно проводить никаких исследований. Подумай об этом. Строка это класс. Пустой является экземпляром строки. - Senfo 3 февраля 2009 года в 17:12

Есть кое-что, чего я не совсем понимаю: как статический конструктор класса String может создать экземпляр класса String? Разве это не сценарий "курица или яйцо"? - DrJokepu 3 февраля 2009 года в 17:12 5

Этот ответ будет правильным почти для любого другого класса, кроме System.String. В .NET много специальных возможностей для обработки строк, и одним из них является то, что вы МОЖЕТЕ иметь строковые константы, просто попробуйте. В этом случае у Джеффа Йейтса правильный ответ. - Джоэл Мюллер 3 февраля 2009 года в 19:25

Как описано в §7.18, константное выражение - это выражение, которое может быть полностью оценено во время компиляции. Поскольку единственный способ создать ненулевое значение ссылочного типа, отличного от строки, это применить оператор new, а так как новый оператор не разрешен в выражении константы, единственно возможное значение для констант ссылочных типов кроме строки ноль. Предыдущие два комментария были взяты непосредственно из спецификации языка C # и повторяют то, что упоминал Джоэл Мюллер. - Senfo 4 февраля 2009 в 15:05 5

Гарри Шутлер
источник
Пожалуйста, проголосуйте за правильный ответ. Если вы перейдете в Определение, вы обнаружите, что он принадлежит классу String и является экземпляром String. Тот факт, что он отображается в нижнем регистре, является магией компилятора.
Гарри Шатлер
Это был не я, кто проголосовал против вас, но в .NET (в отличие от Java) строка и строка в точности совпадают. И да, вы можете иметь строковые литеральные константы в .NET
Tamas Czinege
10
Этот ответ будет правильным почти для любого другого класса, кроме System.String. В .NET много специальных возможностей для обработки строк, и одним из них является то, что вы МОЖЕТЕ иметь строковые константы, просто попробуйте. В этом случае у Джеффа Йейтса правильный ответ.
Джоэл Мюллер
7
Я чуть не удалил этот ответ, так как пришел намного лучший, но обсуждение в этих комментариях стоит того.
Гарри Шатлер
1
@ Гарри, тебе повезло, я прочитал твой последний комментарий, иначе я бы тоже понизил голос. Строка имеет специальную особенность в .NET, которая является классом ref и может быть константой.
Шимми Вайцхандлер,