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

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

19 ответов

Решение

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

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

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

См. .NET спецификацию для получения дополнительной информации.

В чем разница между 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

Предыдущие ответы были правильными для.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), появляются в одной сборке, эти строковые литералы ссылаются на один и тот же экземпляр строки.

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

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

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

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

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

Заметки:

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

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

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

Другое отличие состоит в том, что 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 
}

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

использование String.Empty скорее, чем "" ,

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

Ссылка: String.Empty против ""

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

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

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

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

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

string mystring = "";
ldstr ""

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

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

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

Я склонен использовать String.Empty вместо "" потому что имхо это понятнее и меньше VB-иш.

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

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

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


по умолчанию (строка) +...

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

"" +...

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 +...

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

Когда вы визуально просматриваете код, "" становится раскрашенным так же, как раскрашены строки. 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;

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

public void test(int i=0,string s="")
    {
      // Function Body
    }

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

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

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

Редактировать: [Обязательный (AllowEmptyStrings = true)] решит эту проблему, позволив string.Empty для проверки.

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

Для параметров по умолчанию все равно придется использовать"", но в таком случае мне интересно, почему метод не используетstring? = nullвместо этого для параметра по умолчанию. (также более читабельно для слабовидящих)

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

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

if string + "" = ""

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

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


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

Таким образом, кажется, что "Null" означает абсолютно void, а "String.Empty" означает, что оно содержит какое-то значение, но оно пустое.

Другие вопросы по тегам