Ответ
В C# оператор == для типа string перегружен (string.op_Equality). Это сравнение по значению, а не по ссылкам, что интуитивно понятнее для работы с текстом.
Как это работает
Перегруженный оператор == вызывает статический метод string.Equals(string a, string b), который сравнивает содержимое строк.
string str1 = "hello";
string str2 = "hello";
string str3 = new string(new char[] { 'h', 'e', 'l', 'l', 'o' }); // Новый объект в куче
string str4 = "HELLO";
string str5 = null;
// Сравнение значений (перегруженный ==)
Console.WriteLine(str1 == str2); // True: значения одинаковы
Console.WriteLine(str1 == str3); // True: значения одинаковы, несмотря на разные объекты
Console.WriteLine(str1 == str4); // False: регистр имеет значение
Console.WriteLine(str1 == str5); // False (str1 не null, str5 - null)
Console.WriteLine(str5 == null); // True (корректная обработка null)
// Сравнение ссылок (object.ReferenceEquals)
Console.WriteLine(ReferenceEquals(str1, str2)); // True: из-за интернирования строк
Console.WriteLine(ReferenceEquals(str1, str3)); // False: разные объекты в памяти
Ключевые особенности и лучшие практики
- Null-безопасность: Оператор корректно обрабатывает сравнение с
nullбез вызова исключенияNullReferenceException. - Регистрозависимость: Сравнение чувствительно к регистру. Для регистронезависимого сравнения используйте:
bool equal = string.Equals(str1, str4, StringComparison.OrdinalIgnoreCase); // Или bool equal = str1.Equals(str4, StringComparison.OrdinalIgnoreCase); - Интернирование строк: Для строковых литералов (как
str1иstr2) CLR использует интернирование, помещая их в пул строк. ПоэтомуReferenceEqualsдля них тоже может возвращатьtrue, но на это не стоит полагаться. - Явное сравнение для сложных сценариев: Для сравнения с контролем культуры или типа сравнения всегда предпочтительнее использовать метод
string.Equalsс явным указаниемStringComparison.// Хорошо: явное указание типа сравнения (быстрое, без учета культуры) bool areEqual = string.Equals(str1, str2, StringComparison.Ordinal); - Отличие от
object.==: Дляstringоператор ведет себя иначе, чем стандартный==дляobject. Для ссылочных типов (кромеstring)==по умолчанию сравнивает ссылки, если он не перегружен.
Вывод: Перегрузка == для string — это синтаксический сахар, делающий код чище. Внутри он делегирует вызов методу string.Equals, обеспечивающему семантическое сравнение строк.