В чем разница между String.Empty и (пустой строкой)?

В .NET, в чем разница между String.Empty и "", и являются ли они взаимозаменяемыми, или есть какие-то базовые ссылки или проблемы с локализацией, связанные с равенством, которые String.Empty гарантируют, что это не проблема?


person johnc    schedule 30.09.2008    source источник
comment
Настоящий вопрос заключается не в том, что, а в том, почему. Почему Microsoft придумала string.Empty и что послужило основанием для объявления его как readonly вместо const.   -  person user1451111    schedule 07.07.2018


Ответы (18)


В .NET до версии 2.0 "" создает объект, а string.Empty не создает объекта ref, что делает string.Empty более эффективным.

В версии 2.0 и более поздних версиях .NET все вхождения "" относятся к одному и тому же строковому литералу, что означает, что "" эквивалентно .Empty, но все же не так быстро, как .Length == 0.

.Length == 0 - самый быстрый вариант, но .Empty позволяет получить немного более чистый код.

Дополнительную информацию см. В спецификации .NET.

person Brian R. Bondy    schedule 30.09.2008
comment
в любом случае создал бы объект только один раз из-за интернирования строки. По сути, компромисс с производительностью - пустяки - важнее удобочитаемость. - person Jon Skeet; 15.01.2009
comment
Интересно, что, хотя в вопросе ничего не говорится о сравнении строки со строкой или строкой. Пусто для проверки наличия пустых строк, многие люди, похоже, интерпретируют вопрос именно так ... - person peSHIr; 15.01.2009
comment
Я был бы осторожен с .Length == 0, так как он может вызвать исключение, если ваша строковая переменная равна нулю. Если вы проверите его, он просто вернет false без исключения. - person Jeffrey Harmon; 04.08.2015
comment
@JeffreyHarmon: Или вы могли бы использовать string.IsNullOrEmpty( stringVar ). - person Flynn1179; 27.08.2015
comment
Если кто-то хочет предотвратить плохие методы проверки пустой строки, вы можете включить CA1820 в Анализе кода. docs.microsoft.com/ visualstudio / code-quality / - person sean; 12.08.2017

в чем разница между String.Empty и "", и взаимозаменяемы ли они

string.Empty - это поле только для чтения, а "" - это поле постоянная времени компиляции. Места, где они ведут себя по-разному:

Значение параметра по умолчанию в C # 4.0 или выше

void SomeMethod(int ID, string value = string.Empty)
// Error: Default parameter value for 'value' must be a compile-time constant
{
    //... implementation
}

Выражение регистра в операторе switch

string str = "";
switch(str)
{
    case string.Empty: // Error: A constant value is expected. 
        break;

    case "":
        break;

}

Аргументы атрибутов

[Example(String.Empty)]
// Error: An attribute argument must be a constant expression, typeof expression 
//        or array creation expression of an attribute parameter type
person Habib    schedule 04.12.2012
comment
Интересно, я не думал, что этот старый вопрос получит какую-либо актуальную новую информацию. я ошибался - person johnc; 05.12.2012
comment
Я думаю, что ваш пример №1 Значение параметра по умолчанию в C # 4.0 или выше по сути является дубликатом вашего примера №3 Аргументы атрибута < / b>, поскольку я считаю, что .NET разворачивает параметры по умолчанию в атрибуты. В общем, вы просто не можете поместить значение (времени выполнения) (в данном случае, экземпляр handle, для сторонников) в метаданные (времени компиляции). - person Glenn Slayden; 31.01.2019
comment
@GlennSlayden, я с тобой не согласен. Инициализация атрибута отличается от обычной инициализации. Это потому, что в большинстве случаев вы можете передать String.Empty в качестве аргумента. Вы правы в том, что все примеры показывают это you simply can't put a (run-time) "value" into (compile-time) metadata, и именно это и стремятся показать примеры. - person Sasuke Uchiha; 04.06.2020

Предыдущие ответы были правильными для .NET 1.1 (посмотрите дату публикации, на которую они ссылаются: 2003). Начиная с .NET 2.0 и новее, разницы практически нет. В любом случае JIT будет ссылаться на один и тот же объект в куче.

Согласно спецификации C #, раздел 2.4.4.5: http://msdn.microsoft.com/en-us/library/aa691090(VS.71).aspx

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

Кто-то даже упоминает об этом в комментариях к посту Брэда Абрама.

Таким образом, практический результат "" vs. String.Empty равен нулю. В конце концов, JIT разберется.

Лично я обнаружил, что JIT намного умнее меня, и поэтому я стараюсь не слишком умничать с подобными оптимизациями микрокомпиляторов. JIT будет разворачивать циклы for (), удалять избыточный код, встроенные методы и т. Д. Лучше и в более подходящее время, чем я или компилятор C # могли когда-либо предвидеть. Пусть JIT сделает свое дело :)

person chadmyers    schedule 30.09.2008
comment
Маленькая опечатка? В любом случае JIT будет ссылаться на тот же объект в справке. Вы имели ввиду в куче? - person Dana; 30.09.2008
comment
Связанный популярный вопрос stackoverflow.com/questions/263191/ - person Michael Freidgeim; 31.03.2013

String.Empty - это поле только для чтения, а "" - const. Это означает, что вы не можете использовать String.Empty в операторе switch, потому что он не является константой.

person James Newton-King    schedule 01.10.2008
comment
с появлением ключевого слова default мы можем повысить удобочитаемость без него, предотвратить случайное изменение и иметь константу времени компиляции, хотя, честно говоря, я по-прежнему считаю String.Empty более читабельным, чем по умолчанию, но медленнее для ввода - person Enzoaeneas; 11.02.2020
comment
@Enzoaeneas Строка default пуста, а не пуста. default ко всем ссылочным объектам имеет значение NULL. - person Sorensen; 18.09.2020

Другое отличие состоит в том, что String.Empty генерирует более крупный код CIL. Хотя код для ссылки на "" и String.Empty имеет одинаковую длину, компилятор не оптимизирует конкатенацию строк (см. сообщение в блоге) для аргументов String.Empty. Следующие эквивалентные функции

string foo()
{
    return "foo" + "";
}
string bar()
{
    return "bar" + string.Empty;
}

генерировать этот IL

.method private hidebysig instance string foo() cil managed
{
    .maxstack 8
    L_0000: ldstr "foo"
    L_0005: ret 
}
.method private hidebysig instance string bar() cil managed
{
    .maxstack 8
    L_0000: ldstr "bar"
    L_0005: ldsfld string [mscorlib]System.String::Empty
    L_000a: call string [mscorlib]System.String::Concat(string, string)
    L_000f: ret 
}
person Bruno Martinez    schedule 30.06.2013
comment
Верная точка зрения, но кто бы мог такое сделать? Почему я должен специально объединять пустую строку с чем-то? - person Robert S.; 27.10.2017
comment
@RobertS. Возможно, вторая строка была в отдельной функции, которая была встроена. Я согласен, это случается редко. - person Bruno Martinez; 27.10.2017
comment
не так уж редко используется тернарный оператор: "bar " + (ok ? "" : "error") - person symbiont; 29.01.2019
comment
Но в случае тернарного оператора, если ok не является константой времени компиляции, результирующий IL все равно должен будет использовать string.Concat. - person Sorensen; 18.09.2020

Я предпочитаю использовать String.Empty, а не "" по одной простой, но не очевидной причине: "" и "" НЕ одно и то же, первый фактически содержит 16 символов нулевой ширины. Очевидно, что ни один компетентный разработчик не собирается вставлять символы нулевой ширины в свой код, но если они все же попадут туда, это может стать кошмаром для обслуживания.

Примечания:

  • В этом примере я использовал U + FEFF.

  • Не уверен, что SO собирается съесть эти символы, но попробуйте сами с одним из множества символов нулевой ширины.

  • Я обнаружил это только благодаря https://codegolf.stackexchange.com/

person Justinw    schedule 25.11.2015
comment
Это заслуживает большего количества голосов. Я видел, как эта проблема возникает из-за системной интеграции между платформами. - person EvilDr; 05.12.2018

Вышеупомянутые ответы технически верны, но то, что вы действительно можете использовать для лучшей читаемости кода и наименьшей вероятности исключения, - это String.IsNullOrEmpty (s)

person Eugene Katz    schedule 30.09.2008
comment
Что касается сравнения равенства, я полностью согласен, но вопрос также касался разницы между двумя концепциями, а также сравнения - person johnc; 30.09.2008
comment
Помните, что наименьшая вероятность исключения часто означает наибольшую вероятность продолжения, но что-то не так. Например, если у вас есть аргумент командной строки, который вы анализируете, и кто-то вызывает ваше приложение с --foo=$BAR, тогда вы, вероятно, захотите заметить разницу между ними, которые забывают установить env var, и они вообще не передают флаг. string.IsNullOrEmpty часто свидетельствует о том, что вы неправильно проверили вводимые данные или делаете странные вещи. Обычно вам не следует принимать пустые строки, когда вы действительно хотите использовать null или что-то вроде типа Maybe / Option. - person Alastair Maw; 23.10.2018

Используйте String.Empty вместо "".

Это больше для скорости, чем использования памяти, но это полезный совет. "" - это литерал, поэтому он будет действовать как литерал: при первом использовании он создается, а для следующих применений возвращается его ссылка. В памяти будет храниться только один экземпляр "" независимо от того, сколько раз мы его используем! Я не вижу здесь штрафов за память. Проблема в том, что каждый раз, когда используется "", выполняется цикл сравнения, чтобы проверить, находится ли "" уже в внутреннем пуле. С другой стороны, String.Empty является ссылка на "", хранящийся в зоне памяти .NET Framework. String.Empty указывает на один и тот же адрес памяти для приложений VB.NET и C #. Так зачем искать ссылку каждый раз, когда вам нужно "", когда она есть в String.Empty?

Ссылка: String.Empty vs ""

person Mojtaba Rezaeian    schedule 11.02.2016
comment
Это не было правдой с .net 2.0. - person nelsontruran; 24.06.2019

String.Empty не создает объект, тогда как "" создает. Разница, как указано здесь, тривиальна, тем не мение.

person Esteban Araya    schedule 30.09.2008
comment
Нетривиально проверить строку на наличие string.Empty или. Как указал Юджин Кац, действительно следует использовать String.IsNullOrEmpty. В противном случае вы получите неожиданный результат. - person Sigur; 27.02.2014

Все экземпляры "" являются одним и тем же интернированным строковым литералом (или должны быть). Таким образом, вы действительно не будете бросать новый объект в кучу каждый раз, когда будете использовать "", а просто создадите ссылку на тот же интернированный объект. Сказав это, я предпочитаю string.Empty. Я думаю, это делает код более читабельным.

person Jason Jackson    schedule 30.09.2008

Это не имеет значения!

Некоторое прошлое обсуждение этого:

http://www.codinghorror.com/blog/archives/000185.html

http://blogs.msdn.com/brada/archive/2003/04/22/49997.aspx

http://blogs.msdn.com/brada/archive/2003/04/27/50014.aspx

person Cade Roux    schedule 30.09.2008
comment
Пожалуйста, включите какое-нибудь встроенное объяснение. - person lpapp; 20.02.2014

string mystring = "";
ldstr ""

ldstr подталкивает новую ссылку на объект к строковому литералу, хранящемуся в метаданных.

string mystring = String.Empty;
ldsfld string [mscorlib]System.String::Empty

ldsfld помещает значение статического поля в стек оценки

Я предпочитаю использовать String.Empty вместо "", потому что, IMHO, он более понятен и менее VB-иш.

person Salvuccino    schedule 17.04.2015

Когда вы визуально просматриваете код, "" становится раскрашенным так же, как раскрашены строки. string.Empty выглядит как обычный доступ к члену класса. Во время беглого взгляда легче уловить "" или интуитивно понять значение.

Найдите строки (раскраска переполнения стека точно не помогает, но в VS это более очевидно):

var i = 30;
var f = Math.Pi;
var s = "";
var d = 22.2m;
var t = "I am some text";
var e = string.Empty;
person K0D4    schedule 07.05.2019
comment
Вы правильно подметили, но действительно ли разработчик имел в виду? Возможно, они хотели вложить какую-то еще неизвестную ценность и забыли вернуть? string.Empty дает вам уверенность в том, что исходный автор действительно имел в виду string.Empty. Я знаю, что это мелочь. - person mark_h; 13.05.2019
comment
Кроме того, злонамеренный (или неосторожный) разработчик может поместить символ нулевой ширины между кавычками вместо использования соответствующей escape-последовательности, например \u00ad. - person Palec; 18.05.2019

Эрик Липперт написал (17 июня 2013 г.):
« Первым алгоритмом, над которым я когда-либо работал в компиляторе C #, был оптимизатор, который обрабатывает конкатенации строк. К сожалению, мне не удалось перенести эти оптимизации в кодовую базу Roslyn перед тем, как я ушел; надеюсь, кто-нибудь дойдет до этого! "

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

Однако обратите внимание, в частности, что только один из этих примеров фактически вызывает String.Concat, и я предполагаю, что это из соображений неясной правильности (в отличие от надзора за оптимизацией). Остальные различия объяснить сложнее.


по умолчанию (String) + {default (String), "", String.Empty}

static String s00() => default(String) + default(String);
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s01() => default(String) + "";
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s02() => default(String) + String.Empty;
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

"" + {по умолчанию (String), "", String.Empty}

static String s03() => "" + default(String);
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s04() => "" + "";
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    add  rsp,28h
    ret

static String s05() => "" + String.Empty;
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

String.Empty + {по умолчанию (String), "", String.Empty}

static String s06() => String.Empty + default(String);
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

static String s07() => String.Empty + "";
    mov  rax,[String::Empty]
    mov  rax,qword ptr [rax]
    mov  rdx,rax
    test rdx,rdx
    jne  _L
    mov  rdx,rax
_L: mov  rax,rdx
    add  rsp,28h
    ret

static String s08() => String.Empty + String.Empty;
    mov  rcx,[String::Empty]
    mov  rcx,qword ptr [rcx]
    mov  qword ptr [rsp+20h],rcx
    mov  rcx,qword ptr [rsp+20h]
    mov  rdx,qword ptr [rsp+20h]
    call F330CF60                 ; <-- String.Concat
    nop
    add  rsp,28h
    ret


Сведения о тесте

Microsoft (R) Visual C# Compiler version 2.10.0.0 (b9fb1610)
AMD64 Release
[MethodImpl(MethodImplOptions.NoInlining)]
'SuppressJitOptimization' = false
person Glenn Slayden    schedule 31.01.2019

Исходя из этого с точки зрения Entity Framework: EF версии 6.1.3, похоже, по-разному обрабатывают String.Empty и "" при проверке.

string.Empty обрабатывается как нулевое значение для целей проверки и выдаст ошибку проверки, если оно используется в обязательном (атрибутированном) поле; где "" пройдет проверку и не выдаст ошибку.

Эта проблема может быть решена в EF 7+. Ссылка: - https://github.com/aspnet/EntityFramework/issues/2610 ).

Изменить: [Required (AllowEmptyStrings = true)] решит эту проблему, разрешив проверку string.Empty.

person Anthony    schedule 09.03.2017

Поскольку String.Empty не является константой времени компиляции, вы не можете использовать ее в качестве значения по умолчанию в определении функции.

public void test(int i=0,string s="")
    {
      // Function Body
    }
person tsiva124    schedule 14.03.2017
comment
Просто краткое изложение этого ответа: public void test(int i=0, string s=string.Empty) {} не будет компилироваться и скажет, что значение параметра по умолчанию для 's' должно быть константой времени компиляции. Ответ OP работает. - person bugybunny; 26.10.2018

Спасибо за очень информативный ответ.

Простите мое незнание, если я ошибаюсь. Я использую VB, но я думаю, что если вы проверите длину неназначенной строки (то есть IS Nothing), она вернет ошибку. Я начал программировать в 1969 году, так что я сильно отставал, однако я всегда тестировал строки, объединяя пустую строку (). Например. (на любом языке): -

if string + "" = ""

person kingletit    schedule 12.03.2021

Все здесь дали хорошие теоретические пояснения. У меня было аналогичное сомнение. Итак, я попробовал на нем базовый код. И я нашел разницу. Вот в чем разница.

string str=null;
Console.WriteLine(str.Length);  // Exception(NullRefernceException) for pointing to null reference. 


string str = string.Empty;
Console.WriteLine(str.Length);  // 0

Кажется, что «Null» означает абсолютно пустое значение, а «String.Empty» означает, что он содержит какое-то значение, но он пуст.

person DeepakTheGeek    schedule 21.01.2016
comment
Обратите внимание, что вопрос касается "" по сравнению с string.Empty. Только при попытке определить, пуста ли строка, было упомянуто null. - person Palec; 22.01.2016