Может ли две одинаковые строки быть двумя отдельными экземплярами в С#?
В С# строки интернированы. То есть, если я создам строку foobar
и использую ее во второй раз, у С# будет только один экземпляр строки в памяти, и хотя у меня будет две ссылки, они оба будут указывать на тот же самый экземпляр строки. Это одна из причин, почему строки являются и должны быть неизменными в С#.
Теперь, мой вопрос: возможно ли каким-то образом создать две идентичные строки, чтобы они не были интернированы, но в итоге у нас есть два разных экземпляра строки в памяти с двумя разными адресами, которые содержат один и тот же текст
Если да, то как?
И, это что-то, что может случиться случайно, или вам нужно явно построить сценарий для этого случая?
И, наконец: Предположим, что в памяти есть два отдельных экземпляра строки с одинаковым значением, равны ли они (в терминах ==
)? Если да, то как работает ==
? Сначала сравните по ссылке, затем по значению или...?
Ответы
Ответ 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)
}