Подтвердить что ты не робот

Может ли две одинаковые строки быть двумя отдельными экземплярами в С#?

В С# строки интернированы. То есть, если я создам строку foobar и использую ее во второй раз, у С# будет только один экземпляр строки в памяти, и хотя у меня будет две ссылки, они оба будут указывать на тот же самый экземпляр строки. Это одна из причин, почему строки являются и должны быть неизменными в С#.

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

Если да, то как?

И, это что-то, что может случиться случайно, или вам нужно явно построить сценарий для этого случая?

И, наконец: Предположим, что в памяти есть два отдельных экземпляра строки с одинаковым значением, равны ли они (в терминах ==)? Если да, то как работает ==? Сначала сравните по ссылке, затем по значению или...?

4b9b3361

Ответ 1

В С# строки интернированы.

Нет. В строках С# допускается интернирование. Это совсем другое утверждение.

То есть, если я создаю строку foobar и использую ее во второй раз, у С# будет только один экземпляр строки в памяти, и хотя у меня будет две ссылки, они оба будут указывать на тот же самый экземпляр строки

Нет. Опять же, на С# среде выполнения разрешено решать, что один "foobar" является тем же самым, что и другой, и ставьте их, но этого не требуется.

Конечно, если вы скопируете ссылку, ссылка будет скопирована. Но если вы создаете вторую строку, которая выглядит так же, как и более ранняя строка, нет необходимости, чтобы она была интернирована.

На практике строки интернируются, когда они являются литералами:

string x = "foobar";
string y = "foobar";
// x is reference equal to y

или когда они могут быть вычислено как идентичное компилятору:

string x = "foobar";
string y = "foo" + "bar";
// x is reference equal to y

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

string x = "foobar";
string y = "f" + x.Substring(1); 
// x and y are not reference equal

Ответ 2

Только строковые литералы интернированы. Интерполяция во время выполнения является дорогостоящей, поэтому динамически созданные строки не интернированы (если вы не ставите их явно, вызывая String.Intern).

Следующие строки - это разные экземпляры (вы можете проверить это с помощью object.ReferenceEquals()):

string str1 = "foo";
string str2 = "FOO".ToLower();
string str3 = new StringBuilder().Append("f").Append("oo").ToString();

Оператор == перегружен для string, чтобы сравнить их по значению, а не по ссылке

public static bool operator == (String a, String b) 
{
   return String.Equals(a, b);
}

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

string str1 = "foo";
string str2 = "FOO".ToLower();
object str3 = str2;
bool valueComparison = str1 == str2;     // true - the same value
bool referenceComparison = str1 == str3; // false - different instances

Ответ 3

Вот очень простой тест, доказывающий, что 2 эквивалентные строки не всегда указывают на одну и ту же ссылку на объект:

static void Main(string[] args)
{
    string str1 = "foo";
    string str2 = "f";
    str2 += "oo";
    Console.WriteLine(str1 == str2); // prints true (value equality check)
    Console.WriteLine(object.ReferenceEquals(str1, str2)); // prints false (reference equality check)
}