Чому не String.Порожняйте постійною?


188

У .Net чому String.Empty читається лише замість константи? Мені просто цікаво, чи хтось знає, які міркування стояли за цим рішенням.


5
Це питання може вирішити це, коротка відповідь - ніхто не знає ...
gdoron підтримує Моніку

Так, +1 за відповідь Еріка Ліпперта, дякую!
travis

Особливо враховуючи, що Decimal.Zero - це const (з точки зору користувача, який є ...)
Hamish Grubijan

Відповіді:


148

Причина, яка static readonlyвикористовується замість цього const, пов’язана з використанням некерованого коду, про що вказує Microsoft у випуску загальної мовної інфраструктури загальної мови 2.0 . Файл для перегляду є sscli20\clr\src\bcl\system\string.cs.

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

Позначення цього буквально означатиме, що воно не відображається як поле, до якого ми можемо отримати доступ з рідних.

Я знайшов цю інформацію з цієї зручної статті у CodeProject .


Я дуже вдячний, якщо ви можете пояснити цей коментар (оскільки Джон Скіт не зміг ...) дивіться тут: stackoverflow.com/questions/8462697/…
gdoron підтримує Моніка

2
@gdoron: Моя здогадка (і це здогад) - це це. Коли значення визначається як буквальне (константа), його значення вставляється в місцях, на які воно посилається, тоді як, коли воно не визначається як буквальне, джерело значення посилається, а фактичне значення отримується під час виконання. Я підозрюю, що останній може гарантувати, що під час виконання між правильним і .NET під час виконання відбувається правильне розміщення рядка - якби це було буквальним, можливо, нашому компілятору потрібно було б якось перетягнути буквальне значення у свій власний код, який, ймовірно, не є здійсненний. Хоча це все здогади з мого боку.
Джефф Йейтс

7
Це означає, що для використання значень параметрів за замовчуванням у методах потрібно використовувати "", а не рядок. Що трохи дратує.
нікодим13

17
"" може виглядати як помилка, тоді як рядок. Порожній показує навмисний намір
Крістофер Стівенсон

3
@JeffYates Я додам, що той факт, що він не відповідає, вже дратує. Люди побачили б решту коду і задалися питанням, "чому він використовує тут", а не String.Empty? ". Я серйозно String.Emptyроздумую над тим, щоб більше не використовувати цю причину.
julealgon

24

Думаю, тут багато плутанини та поганих реакцій.

Поперше, const поля - це staticчлени ( не члени екземпляра ).

Перевірте розділ 10.4 Константи специфікації мови C #.

Незважаючи на те, що константи вважаються статичними членами, константа декларування не вимагає і не дозволяє статичний модифікатор.

Якщо public constчлени статичні, не можна вважати, що константа створить новий Об'єкт.

Враховуючи це, наступні рядки коду роблять точно те саме, що стосується створення нового Об'єкта.

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

Ось примітка від Microsoft, яка пояснює різницю між двома:

Ключове слово для читання лише відрізняється від ключового слова 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? Це здається, що набагато краще використовувати статичний readonly тоді.
берсеркер

@theberserker добре, але ви маєте всі варіанти для використання.
Арістос

> тоді компільований dll більше не працюватиме однаково, оскільки вартість робить внутрішній код фактично зберігати копію рядка під час кожного виклику. @Aristos Це не зовсім правильно. Після того, як код буде скомпільований, на його "копію" рядок буде посилатися блок TEXT виконуваного файлу, і весь код буде просто посилатися на той самий блок пам'яті. Те, що ви цитували на своєму другому кроці, - це просто проміжний крок.
Пітер Долкенс

@ user1533523 дякую за замітку - я зроблю тест, коли знайду час, щоб перевірити це
Арістос

Як ви отримали цей збірний код? C # не збирається на збірку!
jv110

0

Ця відповідь існує в історичних цілях.

Спочатку:

Тому що Stringце клас і тому не може бути константою.

Розширене обговорення:

Для перевірки цієї відповіді було забито багато корисного діалогу, і замість того, щоб видалити цей вміст, відтворюється безпосередньо:

У .NET (на відміну від Java) рядок і String точно однакові. І так, ви можете мати лінійні лінійні константи в .NET - DrJokepu 3 лютого '09 о 16:57

Ви говорите, що клас не може мати константи? - StingyJack 3 лютого '09 о 16:58

Так, об’єкти повинні використовуватись лише для читання. Лише структури можуть робити константи. Я думаю, що коли ви використовуєте stringзамість Stringкомпілятора, це змінює const на лише для читання. Все, що стосується задоволення програмістів C. - Гаррі Шутлер 3 лютого '09 о 16:59

tvanfosson просто пояснив це дещо докладніше. "X не може бути константою, тому що містить Y - клас", був трохи занадто контекстним;) - Леонідас 3 лютого '09 о 17:01

string.Empty - це статична властивість, яка повертає екземпляр класу String, а саме порожній рядок, а не сам клас string. - tvanfosson 3 лютого '09 о 17:01

Empty - це лише екземпляр, що читається (це не властивість) класу String. - сенфо 3 лютого '09 о 17:02

Болить голова. Я все ще думаю, що я правий, але зараз я менш впевнений. Дослідження потрібно сьогодні! - Гаррі Шутлер 3 лютого '09 о 17:07

Порожній рядок - це екземпляр класу string. Empty - це статичне поле (не властивість, я виправлений) класу String. В основному різниця між вказівником та річчю, на яку він вказує. Якщо це не було прочитано лише ми, ми могли б змінити, на який екземпляр посилається Порожнє поле. - tvanfosson 3 лютого '09 о 17:07

Гаррі, не потрібно робити жодних досліджень. Подумай над цим. Рядок - це клас. Empty - це екземпляр String. - сенфо 3 лютого '09 о 17:12

Щось мені не зовсім зрозуміло: як на землі може статичний конструктор класу String створити екземпляр класу String? Це не якийсь сценарій "курка чи яйце"? - DrJokepu 3 лютого '09 о 17:12 5

Ця відповідь буде правильною майже для будь-якого іншого класу, крім System.String. .NET має багато спеціальних кожухів для рядків, і одна з них полягає в тому, що ви МОЖЕТЕ мати струнні константи, просто спробуйте. У цьому випадку Джефф Йейтс має правильну відповідь. - Джоель Мюллер 3 лютого '09 о 19:25

Як описано в §7.18, постійний вираз - це вираз, який можна повністю оцінити під час компіляції. Оскільки єдиним способом створення ненульового значення посилального типу, відмінного від рядка, є застосування нового оператора, а оскільки новий оператор не дозволений у постійному виразі, єдине можливе значення для констант типів посилань крім рядка є нульовим. Попередні два коментарі були взяті безпосередньо із специфікації мови C # і підтверджують те, що згадував Джоел Мюллер. - сенфо 4 лютого '09 о 15:05 5


Будь ласка, проголосуйте правильну відповідь. Якщо ви перейдете до визначення, ви побачите, що він знаходиться в класі String і є екземпляром String. Той факт, що він відображається як малі регістри - це магія компілятора.
Гаррі Шутлер

Це не я, хто тебе прихильнив, але в .NET (на відміну від Java) рядок і String точно такі самі. І так, ви можете мати лінійні лінійні константи в .NET
Тамас Czinege

10
Ця відповідь буде правильною майже для будь-якого іншого класу, крім System.String. .NET має багато спеціальних кожухів для рядків, і одна з них полягає в тому, що ви МОЖЕТЕ мати струнні константи, просто спробуйте. У цьому випадку Джефф Йейтс має правильну відповідь.
Джоель Мюллер

7
Я ледве не видалив цю відповідь, оскільки набагато краща була, але обговорення в цих коментарях варто продовжувати.
Гаррі Шутлер

1
@Garry, вам пощастило, що я прочитав ваш останній коментар, інакше я також став би голосом. Рядок має особливість у .NET, що eventho це клас ref, це може бути const.
Шиммі Вайцхандлер
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.