Відмінності методів порівняння рядків у C #


261

Порівнювати рядок у C # досить просто. Насправді існує кілька способів зробити це. Я перерахував деякі в блоці нижче. Мені цікаво, чи є різниці між ними, і коли один слід використовувати над іншими? Слід уникати будь-якої ціни? Чи є ще я не перелічений?

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

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

(Примітка. Я шукаю рівності в цьому прикладі не менше або більше, але сміливо коментуйте це також)


4
Одна пастка - це ви не можете робити stringValue.Equals (null), оскільки це передбачає, що ви можете викликати метод null
johnc


@RobertHarvey Причина, коли я приходжу до stackoverflow, полягає в тому, що мені не потрібно читати кілька сторінок для відповідей.
Syaiful Nizam Yahya

@Syaiful: Причина, коли я приходжу до переповнення стека, - це знайти відповіді, які відсутні в документації.
Роберт Харві

Відповіді:


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): null не дорівнює null" Lol, я б сказав, що ні. null дорівнює винятку ObjectReferenceNotSet.
Кевін

29
== не є тим самим, що .Equals () ... Оператор == викликає статичний метод Equals (рядок a, рядок b) (який у свою чергу переходить до внутрішнього EqualsHelper для порівняння. Calling .Equals on null рядок отримує нульову посилання виключно, тоді як on == не
Dan C.

2
З іншого боку, .Equals дещо швидше (один менш виклик методу всередині), але менш читабельний - можливо, звичайно :).
Dan C.

Я думав, що '==' зробить порівняння посилань, а object.equals зробить значення порівнянь. Як '==' і string.equals працюють однаково?
amesh

@ LasseV.Karlsen Яка ваша думка String.Compare?
JDandChips

72

Від MSDN:

"Метод CompareTo був розроблений головним чином для використання в операціях сортування або алфавіту. Його не слід застосовувати, коли головна мета виклику методу - визначити, чи є два рядки рівнозначними. "

Вони пропонують використовувати .Equalsзамість того, .CompareToщоб шукати виключно рівність. Я не впевнений, чи є різниця між класом .Equalsі ==для нього string. Я іноді буду використовувати .Equalsабо Object.ReferenceEqualsзамість них ==власні заняття, якщо хтось пізніше прийде і перезначить ==оператора для цього класу.


18
Чи бувало з вами колись? (Перевизначення ==) ... Я бачу це як WAAAAY занадто оборонну програмування =)
Хуану

Так, саме тому зараз я використовую Object.ReferenceEquals, коли шукаю рівність об’єктів :). Це може бути занадто оборонним, але я не маніакальний, і правда, ця ситуація не виникає дуже часто.
Ед С.

Я сумніваюся, що це «захисне кодування» корисне. Що робити, якщо власнику класу потрібно переотримати оператор ==, то дізнається, що його ніхто не використовує?
Дейв Ван ден Ейнде

1
@DaveVandenEynde: Так ... я це писав ще раз. Я не роблю цього регулярно, лише переважаючи.
Ед С.

1
Рекомендації Майкрософт зафіксовані тут: Кращі практики використання рядків у .NET Framework
JJS

50

Якщо вам коли-небудь цікаво розбіжності в методах BCL, Reflector - ваш друг :-)

Я дотримуюся цих рекомендацій:

Точна відповідність: EDIT: Я раніше завжди використовував оператор == за принципом, що всередині рівняння (строка, рядок) оператор object == використовується для порівняння посилань на об'єкт, але здається, що strA.Equals (strB) все ще становить 1-11% швидше в цілому, ніж string.Equals (strA, strB), strA == strB та string.CompareOrdinal (strA, strB). Я перевіряю цикл за допомогою секундоміра на значення інтернованих / не інтернованих рядків, з однаковою / різною довжиною рядка та різними розмірами (від 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

Як сказав Ед , порівняння використовується для сортування.

Однак є різниця між .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 .


Кожен із згаданих методів (та інших) має певне призначення. Ключова відмінність між ними полягає в тому, який тип переліку StringComppare вони використовують за замовчуванням. Є кілька варіантів:

  • Сучасна культура
  • CurrentCultureIgnoreCase
  • Інваріантна культура
  • InvariantCultureIgnoreCase
  • Звичайні
  • OrdinalIgnoreCase

Кожен з наведених вище типів порівняння націлений на різні випадки використання:

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

Зауважимо, що перелік StringComp poredation , а також перевантаження для методів порівняння рядків існує з .NET 2.0.


Метод String.CompareTo (String)

Насправді це безпечна реалізація методу IComparable.CompareTo . Інтерпретація за замовчуванням: CurrentCulture.

Використання:

Метод CompareTo був розроблений головним чином для використання в операціях сортування або алфавіту

Таким чином

Реалізація інтерфейсу IComparable обов'язково використовує цей метод

Метод String.Compare

Статичний член класу String, який має багато перевантажень. Інтерпретація за замовчуванням: CurrentCulture.

По можливості ви повинні викликати перевантаження методу Порівняння, який включає параметр StringComppare.

Метод строкових рівностей

Перезавантажений з класу Object та перевантажений для безпеки типу. Інтерпретація за замовчуванням: Звичайна. Зауважте, що:

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


Клас StringComparer

Існує також інший спосіб вирішення порівнянь рядків, особливо спрямований на сортування:

Клас StringComparer можна використовувати для створення типового порівняння для сортування елементів у загальній колекції. Класи, такі як Hashtable, Dictionary, SortedList і SortedList, використовують клас StringComparer для сортування.


2
Згідно з деякими іншими повідомленнями про SO, усі методи, крім порядкових, мають випадки, коли Порівняння (a, b) та Порівняння (b, a) можуть повернути 1, а помилка класифікується як "не буде виправлено ". Таким чином , я не впевнений , що будь-які такі порівняння мають який - або випадок використання.
supercat

@supercat Ви можете пов’язати це чи навести приклад?
Noctis

1
Дивіться stackoverflow.com/questions/17599084/… для обговорення цього питання.
supercat

7

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

Якщо ваш додаток буде працювати в різних країнах, я рекомендую ознайомитись із наслідками CultureInfo та, можливо, використовувати .Equals. Оскільки я дійсно пишу програми лише для США (і мені все одно, якщо хтось не працює належним чином), я завжди просто використовую ==.


5

У перерахованих тут формах між ними немає великої різниці. CompareToзакінчується виклик CompareInfoметоду, який робить порівняння з використанням поточної культури; Equalsвикликається ==оператором.

Якщо ви вважаєте перевантаження, то справи стають інакшими. Compareі ==може лише використовувати поточну культуру для порівняння рядка. Equalsі String.Compareможе взяти StringComparisonаргумент перерахування, який дозволяє вказати порівняння, що не враховує культуру чи регістр. Тільки String.Compareдозволяє вказати a CultureInfoі виконати порівняння, використовуючи культуру, відмінну від культури за замовчуванням.

Через свою універсальність я вважаю, що я використовую String.Compareбільше, ніж будь-який інший метод порівняння; це дозволяє мені точно вказати, що я хочу.


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");

0
  • s1.CompareTo (s2): НЕ використовуйте, якщо основною метою є визначення того, чи є дві струни еквівалентними
  • s1 == s2: Неможливо ігнорувати регістр
  • s1.Equals (s2, StringComppare): викидає NullReferenceException, якщо s1 недійсний
  • String.Equals (s2, StringCompparement): За допомогою процесу усунення цей статичний метод є ПЕРЕМОЖНИК (припустимо, типовий випадок використання для визначення того, чи є дві струни еквівалентними)!


-9

з .Equals, ви також отримуєте параметри StringComppare. дуже зручно для ігнорування справи та інших речей.

btw, це оцінюватиметься як хибне

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

return a==b

Оскільки == порівнює значення a і b (які є вказівниками), це буде оцінено як істинне лише в тому випадку, якщо покажчики вказують на один і той же об'єкт у пам'яті. .Equals відмежування покажчиків і порівняння значень, збережених у покажчиках. a.Equals (b) тут би було правдою.

і якщо ви зміните b на:

b = "MYSTRING";

тоді a.Equals (b) помилково, але

a.Equals(b, StringComparison.OrdinalIgnoreCase) 

було б правдою

a.CompareTo (b) викликає функцію CompareTo рядка, яка порівнює значення вказівників і повертає <0, якщо значення, збережене в a, менше значення, збережене в b, повертає 0, якщо a.Equals (b) є істинним, і > 0 інакше. Однак це чутливе до регістру, я думаю, є варіанти, які можуть порівняти регістр ігнорувати регістр і подібне, але зараз не встигайте шукати. Як уже говорили інші, це було б зроблено для сортування. Порівняння рівності таким чином призведе до непотрібних витрат.

Я впевнений, що я залишаю речі, але я думаю, що цього має бути достатньо інформації, щоб почати експериментувати, якщо вам потрібно більше деталей.


9
Частина a == b невірна. Оператор == ефективно перевантажується для класу String і він порівнює значення незалежно від реальних посилань.
Goyuix
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.