Различия в методах сравнения строк в C #

261

Сравнение строки в C # довольно просто. На самом деле есть несколько способов сделать это. Я перечислил некоторые в блоке ниже. Что меня интересует, так это различия между ними и когда один должен использоваться над другими? Следует ли избегать любой ценой? Есть еще что я не перечислил?

string testString = "Test";
string anotherString = "Another";

if (testString.CompareTo(anotherString) == 0) {}
if (testString.Equals(anotherString)) {}
if (testString == anotherString) {}

(Примечание: в этом примере я ищу равенство, не меньше и не больше, но также не стесняйтесь комментировать)

Craig
источник
4
Одна ловушка - вы не можете выполнить stringValue.Equals (null), так как это предполагает, что вы можете вызвать метод с нулевым значением
johnc
1
Справочник MSDN
Роберт Харви
@RobertHarvey Причина, по которой я перехожу к stackoverflow, заключается в том, что мне не нужно читать несколько страниц для ответов.
Шайфул Низам Яхья
@Syaiful: причина, по которой я пришел в Stack Overflow, - найти ответы, которых нет в документации.
Роберт Харви

Ответы:

231

Вот правила работы этих функций:

stringValue.CompareTo(otherStringValue)

  1. null перед строкой
  2. он использует CultureInfo.CurrentCulture.CompareInfo.Compare, что означает, что он будет использовать сравнение, зависящее от культуры. Это может означать, что ßбудет сравниваться равным SSв Германии, или аналогичным

stringValue.Equals(otherStringValue)

  1. null не считается равным чему-либо
  2. если вы не укажете StringComparisonопцию, он будет использовать то, что выглядит как прямая проверка порядкового номера, т. е. ßотличается от SSлюбого языка или культуры

stringValue == otherStringValue

  1. Это не то же самое, что stringValue.Equals().
  2. ==Оператор вызывает статический Equals(string a, string b)метод (который , в свою очередь , идет на внутренний , EqualsHelperчтобы сделать сравнение.
  3. Вызов .Equals()на nullстроке получает nullэталонное исключение, а на ==нет.

Object.ReferenceEquals(stringValue, otherStringValue)

Просто проверяет, что ссылки одинаковы, т.е. это не просто две строки с одинаковым содержимым, вы сравниваете строковый объект с самим собой.


Обратите внимание, что с опциями выше, которые используют вызовы методов, есть перегрузки с большим количеством опций, чтобы указать, как сравнивать.

Мой совет, если вы просто хотите проверить на равенство - решите, хотите ли вы использовать сравнение с культурой или нет, а затем использовать .CompareToили .Equals, в зависимости от выбора.

Лассе В. Карлсен
источник
5
"stringValue.Equals (otherStringValue): нуль не равен нулю" Lol, я бы сказал, нет. null равно исключению ObjectReferenceNotSet.
Кевин
29
== - это не то же самое, что .Equals () ... Оператор == вызывает статический метод Equals (строка a, строка b) (который, в свою очередь, обращается к внутреннему EqualsHelper для сравнения. Вызов .Equals для нуля Строка получает нулевую ссылку, исключая, в то время как на == нет.
Дэн С.
2
С другой стороны, .Equals немного быстрее (внутренний вызов на один метод меньше), но менее читабелен - возможно, конечно :).
Дэн С.
Я думал, что '==' будет делать сравнения ссылок, а object.equals будет делать сравнения значений. Как '==' и string.equals работают одинаково?
Amesh
@ LasseV.Karlsen О чем ты думаешь String.Compare?
JDandChips
72

Из MSDN:

«Метод CompareTo был разработан в первую очередь для использования в операциях сортировки или алфавитного размещения. Его не следует использовать, когда основной целью вызова метода является определение того, эквивалентны ли две строки. Чтобы определить, эквивалентны ли две строки, вызовите метод Equals. "

Они предлагают использовать .Equalsвместо того, чтобы .CompareToискать только равенство. Я не уверен, есть ли разница между .Equalsи ==для stringкласса. Иногда я буду использовать .Equalsили Object.ReferenceEqualsвместо ==своих собственных классов, если кто-то придет позже и переопределит ==оператор для этого класса.

Эд С.
источник
18
Это когда-нибудь случалось с тобой? (Переопределение ==) ... Я вижу это как WAAAAY слишком оборонительное программирование =)
Хуану
Да, именно поэтому я теперь использую Object.ReferenceEquals, когда я ищу равенство объектов :). Это может быть немного чрезмерно в обороне, но я не маньяк по этому поводу, и, честно говоря, такая ситуация не возникает очень часто.
Эд С.
Я сомневаюсь, что это «защитное кодирование» полезно. Что если владелец класса должен переопределить оператор ==, а затем выяснить, что никто не использует его?
Дэйв Ван ден Эйнде
1
@DaveVandenEynde: Да ... Я написал это некоторое время назад. Я не делаю это регулярно, только переопределяю.
Эд С.
1
Рекомендация Microsoft записана здесь: Рекомендации по использованию строк в .NET Framework
JJS
50

Если вам когда-нибудь интересно узнать о различиях в методах BCL, Reflector - ваш друг :-)

Я следую этим рекомендациям:

Точное совпадение: РЕДАКТИРОВАТЬ: Ранее я всегда использовал оператор == по принципу, согласно которому внутри Equals (строка, строка) используется оператор object == для сравнения ссылок на объекты, но кажется, что strA.Equals (strB) по-прежнему равен 1-11%. быстрее, чем string.Equals (strA, strB), strA == strB и string.CompareOrdinal (strA, strB). Я проверил цикл с помощью StopWatch как для внутренних, так и для не внутренних значений строки, с одинаковой / различной длиной строки и разными размерами (от 1 до 5 МБ).

strA.Equals(strB)

Человекочитаемое соответствие (западные культуры, без учета регистра):

string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase) == 0

Человекочитаемое соответствие (все другие культуры, нечувствительный кейс / акцент / кана / и т. Д., Определенные CultureInfo):

string.Compare(strA, strB, myCultureInfo) == 0

Человекочитаемое соответствие с пользовательскими правилами (Все другие культуры):

CompareOptions compareOptions = CompareOptions.IgnoreCase
                              | CompareOptions.IgnoreWidth
                              | CompareOptions.IgnoreNonSpace;
string.Compare(strA, strB, CultureInfo.CurrentCulture, compareOptions) == 0
Максимум
источник
18

Как сказал Эд , CompareTo используется для сортировки.

Однако есть разница между .Equals и ==.

== разрешает по существу следующий код:

if(object.ReferenceEquals(left, null) && 
   object.ReferenceEquals(right, null))
    return true;
if(object.ReferenceEquals(left, null))
    return right.Equals(left);
return left.Equals(right);

Простая причина в том, что следующее вызовет исключение:

string a = null;
string b = "foo";

bool equal = a.Equals(b);

И следующего не будет:

string a = null;
string b = "foo";

bool equal = a == b;
Джонатан С Дикинсон
источник
15

Хорошее объяснение и рекомендации по проблемам сравнения строк можно найти в статье Новые рекомендации по использованию строк в Microsoft .NET 2.0, а также в Рекомендации по использованию строк в .NET Framework .


Каждый из упомянутых методов (и других) имеет определенное назначение. Основное различие между ними заключается в том, какой тип перечисления StringComparison они используют по умолчанию. Есть несколько вариантов:

  • CurrentCulture
  • CurrentCultureIgnoreCase
  • InvariantCulture
  • InvariantCultureIgnoreCase
  • порядковый
  • OrdinalIgnoreCase

Каждый из приведенных выше типов сравнения ориентирован на разные варианты использования:

  • порядковый
    • Чувствительные к регистру внутренние идентификаторы
    • Чувствительные к регистру идентификаторы в таких стандартах, как XML и HTTP
    • Чувствительные к регистру настройки безопасности
  • OrdinalIgnoreCase
    • Независимые от регистра внутренние идентификаторы
    • Идентификаторы без учета регистра в таких стандартах, как XML и HTTP
    • Пути к файлам (в Microsoft Windows)
    • Ключи реестра / значения
    • Переменные среды
    • Идентификаторы ресурса (например, для обработки имен)
    • Настройки, не учитывающие регистр безопасности
  • InvariantCulture или InvariantCultureIgnoreCase
    • Некоторые сохранившиеся лингвистически важные данные
    • Отображение лингвистических данных, требующих фиксированного порядка сортировки
  • CurrentCulture или CurrentCultureIgnoreCase
    • Данные отображаются для пользователя
    • Большая часть пользовательского ввода

Обратите внимание, что перечисление StringComparison, а также перегрузки для методов сравнения строк существуют с .NET 2.0.


Метод String.CompareTo (String)

На самом деле это тип безопасной реализации метода IComparable.CompareTo . Интерпретация по умолчанию: CurrentCulture.

Использование:

Метод CompareTo был разработан в первую очередь для использования в операциях сортировки или алфавитирования

таким образом

Реализация интерфейса IComparable обязательно будет использовать этот метод

Метод String.Compare

Статический член класса String, который имеет много перегрузок. Интерпретация по умолчанию: CurrentCulture.

По возможности следует вызывать перегрузку метода Compare, который включает параметр StringComparison.

Метод String.Equals

Переопределяется из класса Object и перегружается для обеспечения безопасности типов. Интерпретация по умолчанию: Порядковый. Заметь:

Методы равенства класса String включают статические Equals , статический оператор == и экземплярный метод Equals .


Класс StringComparer

Существует также другой способ сравнения строк, особенно с целью сортировки:

Вы можете использовать класс StringComparer для создания сравнения по типу для сортировки элементов в общей коллекции. Такие классы, как Hashtable, Dictionary, SortedList и SortedList, используют класс StringComparer для целей сортировки.

Рышард Деган
источник
2
Согласно некоторым другим сообщениям о SO, все методы, кроме обычных, имеют случаи, когда Compare (a, b) и Compare (b, a) могут возвращать 1, и ошибка классифицируется как «не будет исправлена». ». Таким образом, я не уверен, что у таких сравнений есть какой-либо вариант использования.
суперкат
@supercat Можете ли вы дать ссылку на это или привести пример?
Noctis
1
См. Stackoverflow.com/questions/17599084/… для обсуждения этой проблемы.
суперкат
7

Не то, чтобы производительность обычно имела значение в 99% случаев, когда вам нужно это делать, но если бы вам пришлось делать это в цикле несколько миллионов раз, я бы настоятельно рекомендовал использовать .Equals или ==, потому что, как только он находит символ это не совпадает, это выдает все как ложное, но если вы используете CompareTo, ему придется выяснить, какой символ меньше другого, что приведет к немного худшему времени производительности.

Если ваше приложение будет работать в разных странах, я рекомендую вам взглянуть на последствия CultureInfo и, возможно, использовать .Equals. Поскольку я действительно пишу приложения только для США (и мне все равно, если они не будут работать должным образом), я всегда просто использую ==.

viggity
источник
5

В формах, которые вы перечислили здесь, нет большой разницы между ними. CompareToзаканчивает тем, что вызывает CompareInfoметод, который делает сравнение, используя текущую культуру; Equalsназывается== оператором.

Если учесть перегрузки, то все становится иначе. Compareи ==может использовать только текущую культуру для сравнения строки. Equalsи String.Compareможет принимать StringComparisonаргумент перечисления, который позволяет вам задавать сравнения без учета культуры или без учета регистра. String.CompareПозволяет только указать CultureInfoи выполнить сравнение с использованием культуры, отличной от культуры по умолчанию.

Я считаю, что из-за его универсальности я использую String.Compareбольше, чем любой другой метод сравнения; это позволяет мне точно указать, что я хочу.

OwenP
источник
2

Одно большое отличие, которое следует отметить, - .Equals () сгенерирует исключение, если первая строка пуста, тогда как == не будет.

       string s = null;
        string a = "a";
        //Throws {"Object reference not set to an instance of an object."}
        if (s.Equals(a))
            Console.WriteLine("s is equal to a");
        //no Exception
        if(s==a)
            Console.WriteLine("s is equal to a");
Rauld
источник
0
  • s1.CompareTo (s2): НЕ использовать, если основная цель - определить, эквивалентны ли две строки
  • s1 == s2: невозможно игнорировать регистр
  • s1.Equals (s2, StringComparison): генерирует исключение NullReferenceException, если s1 равно нулю
  • String.Equals (s2, StringComparison): в процессе исключения этот статический метод является ПОБЕДИТЕЛЕМ (в предположении типичного варианта использования для определения эквивалентности двух строк)!
Джон ДиФини
источник
-1

Использование .Equals также намного проще для чтения .

hometoast
источник
-9

с .Equals вы также получаете опции StringComparison. очень удобно для игнорирования дела и других вещей.

Кстати, это будет оцениваться как ложный

string a = "myString";
string b = "myString";

return a==b

Поскольку == сравнивает значения a и b (которые являются указателями), это будет иметь значение true, только если указатели указывают на один и тот же объект в памяти. .Equals разыменовывает указатели и сравнивает значения, хранящиеся в указателях. a.Equals (б) будет верно здесь.

и если вы измените b на:

b = "MYSTRING";

тогда a.Equals (b) ложно, но

a.Equals(b, StringComparison.OrdinalIgnoreCase) 

было бы правдой

a.CompareTo (b) вызывает функцию CompareTo строки, которая сравнивает значения в указателях и возвращает <0, если значение, хранящееся в a, меньше значения, хранящегося в b, возвращает 0, если a.Equals (b) имеет значение true, и > 0 в противном случае. Тем не менее, это чувствительно к регистру, я думаю, что есть варианты для CompareTo, чтобы игнорировать регистр и тому подобное, но сейчас нет времени искать. Как уже говорили другие, это будет сделано для сортировки. Сравнение на равенство таким образом привело бы к ненужным накладным расходам.

Я уверен, что я пропускаю вещи, но я думаю, что этого должно быть достаточно, чтобы начать экспериментировать, если вам нужно больше деталей.

Дэвид
источник
9
Часть a == b неверна. Оператор == эффективно перегружен для класса String, и он сравнивает значения независимо от фактических ссылок.
Goyuix