Ответ
В 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, обеспечивающему семантическое сравнение строк.
Ответ 18+ 🔞
А, ну это же классика, про которую все вроде знают, но потом всё равно ебут мозг на собеседовании! Смотри, в C# для строк этот самый == — он не просто так, он перегруженный, ёпта. То есть, когда ты пишешь str1 == str2, ты сравниваешь не адреса в памяти, как для каких-нибудь левых объектов, а именно содержимое, сами буквы. Это ж логично, блядь, мы же с текстом работаем, а не с указателями!
Как оно под капотом работает? Да просто этот оператор вызывает статический метод string.Equals, который уже и проверяет, одинаковые ли символы идут подряд. Всё гениальное — просто, как три копейки.
Вот, смотри на пример, тут всё видно:
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 не пустой, а str5 — нуль)
Console.WriteLine(str5 == null); // True (и с нулём он не сломается, молодец)
// А вот если бы мы сравнивали ссылки (как для обычных объектов)
Console.WriteLine(ReferenceEquals(str1, str2)); // True: из-за интернирования строк, они могут указывать на одно место
Console.WriteLine(ReferenceEquals(str1, str3)); // False: а вот тут уже разные объекты в памяти, хоть и с одним текстом
Теперь про важные штуки, чтобы не облажаться:
- С нулём не падает. Оператор умный, он не вызовет
NullReferenceException, если одна из строк —null. Он просто вернётfalse(илиtrue, если оба нуля). Удобно же. - Регистр — это важно. Для него "Hello" и "hello" — это две большие разницы. Если нужно игнорировать регистр, то используй специальные методы, не выёбывайся:
bool equal = string.Equals(str1, str4, StringComparison.OrdinalIgnoreCase); // Или так bool equal = str1.Equals(str4, StringComparison.OrdinalIgnoreCase); - Интернирование — это магия, на которую не стоит ставить. Да, для одинаковых литералов (
"hello") CLR может использовать один объект в памяти, поэтомуReferenceEqualsиногда возвращаетtrue. Но рассчитывать на это — прямой путь в ад отладки. Думай о значении, а не о ссылке. - Для серьёзных дел — используй явное сравнение. Если тебе важно, как сравнивать (игнорируя культуру или нет), то лучше явно вызывать
string.Equalsс параметромStringComparison. Это профессионально и без сюрпризов.// Вот так — правильно и понятно. Быстрое сравнение, без оглядки на культуру. bool areEqual = string.Equals(str1, str2, StringComparison.Ordinal); - Запомни разницу. Для 99% других объектов в C# оператор
==сравнивает ссылки (если его не перегрузили). А дляstring— значения. Это исключение, которое все запоминают.
Короче, перегрузка == для строк — это такой синтаксический сахар, который делает жизнь проще. Внутри он просто зовёт string.Equals, который всё аккуратно сравнивает. Главное — понимать, что сравниваешь именно текст, а не ячейки памяти, и не забывать про регистр, а то потом волосы на жопе рвать будешь.