Что означает запутанный комментарий над «string.Empty» в источнике .NET / BCL? - PullRequest
33 голосов
/ 13 декабря 2011

Я пытаюсь понять, почему string.Empty это readonly, а не const. Я видел эту запись, но я не понимаю комментарий, который Microsoft написала по этому поводу. Как Джон Скит написал в комментарии "Я не знаю - это не имеет большого смысла, если честно ..."

Общая языковая инфраструктура 2.0 с общим исходным кодом версии 2.0 . string.cs находится в sscli20 \ clr \ src \ bcl \ system \ string.cs

// The Empty constant holds the empty string value.
//We need to call the String constructor so that the compiler doesn't mark this as a literal.
//Marking this as a literal would mean that it doesn't show up as a field which we can access 
//from native.
public static readonly String Empty = ""; 

Я не вижу здесь никакого вызова конструктора String и, кроме того, он помечен как литерал - ""

Может кто-нибудь объяснить, пожалуйста, в виде обычного текста, что означает комментарий и почему string.Empty readonly, а не const?


Update
Эрик Липперт прокомментировал удаленный ответ :

Я спросил одного из старожилов C # за ланчем об этом, и он не вспомнил, почему именно было принято это решение, но предположил, что это как-то связано с интернированием.

1 Ответ

14 голосов
/ 18 декабря 2011

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

Предположим, у вас есть Assembly1.dll, содержащая класс, объявляющий

public static const int SOME_ERROR_CODE=0x10;
public static readonly int SOME_OTHER_ERROR_CODE=0x20;

, и другой класс, использующий это, например,

public int TryFoo() {
    try {foo();}
    catch (InvalidParameterException) {return SOME_ERROR_CODE;}
    catch (Exception) { return SOME_OTHER_ERROR_CODE;}
    return 0x00;
}

Вы компилируетеваш класс в Assembly2.dll и свяжите его с Assembly1.dll, как и ожидалось, ваш метод вернет 0x10 при неверных параметрах, 0x20 при других ошибках, 0x00 при успехе.

Особенно, если вы создаете Assembly3.exe, содержащийчто-то вроде

int errorcode=TryFoo();
if (errorcode==SOME_ERROR_CODE) bar();
else if (errorcode==SOME_OTHER_ERROR_CODE) baz();

Он будет работать как положено (после связи с Assembly1.dll и Assembly2.dll)

Теперь, если вы получаете новую версию Assembly1.dll, которая имеет

public const int SOME_ERROR_CODE=0x11;
public readonly int SOME_OTHER_ERROR_CODE=0x21;

Если перекомпилировать Assembly3.exe и связать последний фрагмент с новым Assembly1.dll и неизмененным Assembly2.dll, он перестанет работать должным образом:

bar () НЕ будет вызыватьсяправильно: Assembly2.dll запоминает LITERAL 0x20, который не совпадает с литералом 0x21, который Assembly3.exe считывает из Assembly1.dll

baz () будет вызван правильно: Both Assembly2.dll и Assembly3.exe ссылаются на ССЫЛКУ СИМВОЛА, называемую SOME_OTHER_ERROR_CODE, которая в обоих случаях разрешена текущей версией Assembly1.dll, поэтому в обоих случаях она равна 0x21.

Короче: a const создает LITERAL, readonly создает SYMBOL REFERENCE.

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

Таким образом,

public static readonly String Empty = ""; 

создает symbol reference (восстанавливается во время первого использования при вызове куленту String), который можно маршалировать и использовать таким образом из native, в то время как

public static const String Empty = ""; 

создаст литерал, который не может.

...