Różnice w metodach porównywania ciągów w języku C #


261

Porównywanie ciągów w C # jest dość proste. W rzeczywistości istnieje kilka sposobów, aby to zrobić. Niektóre wymieniłem w poniższym bloku. Zastanawiam się, jakie są między nimi różnice i kiedy należy używać jednego nad innymi? Czy należy tego unikać za wszelką cenę? Czy jest jeszcze coś, czego nie wymieniłem?

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

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

(Uwaga: szukam równości w tym przykładzie, nie mniej niż lub więcej niż, ale mogę również komentować)


4
Jedną pułapką jest to, że nie można wykonać stringValue.Equals (null), ponieważ zakłada się, że można wywołać metodę na null
jn


@RobertHarvey Powodem, dla którego przyszedłem do stackoverflow, jest to, że nie muszę czytać wielu stron w celu uzyskania odpowiedzi.
Syaiful Nizam Yahya,

@Syaiful: Powodem, dla którego przybyłem do Stack Overflow, jest znalezienie odpowiedzi, których nie ma w dokumentacji.
Robert Harvey,

Odpowiedzi:


231

Oto zasady działania tych funkcji:

stringValue.CompareTo(otherStringValue)

  1. null jest przed sznurkiem
  2. używa CultureInfo.CurrentCulture.CompareInfo.Compare, co oznacza, że ​​użyje porównania zależnego od kultury. Może to oznaczać, że ßbędzie to równe SSw Niemczech lub podobnie

stringValue.Equals(otherStringValue)

  1. null nie jest uważany za coś równego
  2. chyba że określisz StringComparisonopcję, użyje czegoś, co wygląda jak bezpośrednia kontrola równości porządkowej, tj. ßnie jest tym samym, co SSw dowolnym języku lub kulturze

stringValue == otherStringValue

  1. To nie to samo co stringValue.Equals().
  2. ==Operator wywołuje statyczną Equals(string a, string b)metodę (który z kolei przechodzi do wewnętrznego EqualsHelperzrobić porównanie.
  3. Dzwoniąc .Equals()na nullciąg dostaje nullwyjątek odniesienia, podczas gdy na ==nie.

Object.ReferenceEquals(stringValue, otherStringValue)

Po prostu sprawdza, czy referencje są takie same, tzn. Nie są to tylko dwa ciągi o tej samej treści, porównujesz sam obiekt łańcuchowy.


Zauważ, że przy powyższych opcjach, które używają wywołań metod, występują przeciążenia z większą liczbą opcji określających sposób porównywania.

Moja rada, jeśli chcesz po prostu sprawdzić równość, zdecyduj, czy chcesz użyć porównania zależnego od kultury, czy nie, a następnie użyj .CompareTolub .Equals, w zależności od wyboru.


5
"stringValue.Equals (otherStringValue): null nie jest równy null" Lol, powiedziałbym, że nie. null jest równy wyjątkowi ObjectReferenceNotSet.
Kevin

29
== nie jest tym samym, co .Equals () ... Operator == wywołuje statyczną metodę Equals (string a, string b) (która z kolei przechodzi do wewnętrznego EqualsHelper w celu wykonania porównania. string dostaje referencję zerową exc., podczas gdy on == nie.
Dan C.

2
Z drugiej strony .Equals jest nieco szybszy (o jedno wewnętrzne wywołanie mniejszej metody), ale mniej czytelny - prawdopodobnie oczywiście :).
Dan C.

Myślałem, że „==” zrobi porównania referencyjne, a object.equals wykona porównania wartości. Jak „==” i string.equals działają tak samo?
amesh

@ LasseV.Karlsen Jakie jest Twoje zdanie String.Compare?
JDandChips

72

Z MSDN:

„Metoda CompareTo została zaprojektowana przede wszystkim do użycia w operacjach sortowania lub alfabetycznego. Nie należy jej używać, gdy głównym celem wywołania metody jest ustalenie, czy dwa ciągi są równoważne. Aby ustalić, czy dwa ciągi są równoważne, należy wywołać metodę Equals. „

Sugerują używanie .Equalszamiast .CompareToszukania wyłącznie równości. Nie jestem pewien, czy istnieje różnica pomiędzy .Equalsi ==po stringzajęciach. Czasami będę używać .Equalslub Object.ReferenceEqualszamiast ==moich własnych klas, na wypadek gdyby ktoś pojawił się później i przedefiniował ==operatora tej klasy.


18
Czy to ci się kiedykolwiek przytrafiło? (Redefiniowanie ==) ... Widzę go jako Waaaay programowania zbyt defensywny =)
Juan

Tak, dlatego teraz używam Object.ReferenceEquals, gdy szukam równości obiektów :). Może to być trochę zbyt defensywne, ale nie jestem maniakiem i prawdę mówiąc, ta sytuacja nie pojawia się zbyt często.
Ed S.

Wątpię, aby to „kodowanie obronne” było przydatne. Co jeśli właściciel klasy musi zastąpić operator ==, a następnie dowie się, że nikt go nie używa?
Dave Van den Eynde

1
@DaveVandenEynde: Tak ... napisałem to jakiś czas temu. Nie robię tego regularnie, tylko nadpisywanie .Equals, gdy jest to właściwe.
Ed S.

1
Rekomendacja Microsoftu znajduje się tutaj: Najlepsze praktyki korzystania z ciągów w .NET Framework
JJS

50

Jeśli kiedykolwiek jesteś ciekawy różnic w metodach BCL, Reflector jest twoim przyjacielem :-)

Przestrzegam tych wytycznych:

Dopasowanie ścisłe: EDYCJA: Wcześniej zawsze używałem operatora == na zasadzie, że wewnątrz Equals (ciąg, ciąg) operator obiektu == służy do porównywania odniesień do obiektu, ale wydaje się, że strA.Equals (strB) wciąż wynosi 1-11% ogólnie szybciej niż string.Equals (strA, strB), strA == strB i string.CompareOrdinal (strA, strB). Testowałem w pętli za pomocą StopWatch zarówno dla wewnętrznych, jak i nie internowanych wartości łańcuchów, z tymi samymi / różnymi długościami łańcuchów i różnymi rozmiarami (1B do 5 MB).

strA.Equals(strB)

Dopasowanie czytelne dla człowieka (kultury zachodnie, bez rozróżniania wielkości liter):

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

Dopasowanie czytelne dla człowieka (Wszystkie inne kultury, niewrażliwa wielkość liter / akcent / kana / itd. Zdefiniowane przez CultureInfo):

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

Dopasowanie czytelne dla człowieka z niestandardowymi regułami (Wszystkie inne kultury):

CompareOptions compareOptions = CompareOptions.IgnoreCase
                              | CompareOptions.IgnoreWidth
                              | CompareOptions.IgnoreNonSpace;
string.Compare(strA, strB, CultureInfo.CurrentCulture, compareOptions) == 0

18

Jak powiedział Ed , do sortowania używa się CompareTo.

Istnieje jednak różnica między .Equals a ==.

== rozwiązuje zasadniczo następujący kod:

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

Prostym powodem jest wyjątek:

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

bool equal = a.Equals(b);

A następujące nie będą:

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

bool equal = a == b;

15

Dobre objaśnienia i praktyki dotyczące problemów z porównywaniem ciągów można znaleźć w artykule Nowe zalecenia dotyczące używania ciągów w Microsoft .NET 2.0, a także w najlepszych praktykach dotyczących używania ciągów w .NET Framework .


Każda z wymienionych metod (i innych) ma określony cel. Kluczowa różnica między nimi polega na tym, jakiego rodzaju wyliczenia StringComparison używają domyślnie. Istnieje kilka opcji:

  • CurrentCulture
  • CurrentCultureIgnoreCase
  • InvariantCulture
  • InvariantCultureIgnoreCase
  • Porządkowy
  • OrdinalIgnoreCase

Każdy z powyższych typów porównania dotyczy innego przypadku użycia:

  • Porządkowy
    • Wewnętrzne identyfikatory z rozróżnianiem wielkości liter
    • Identyfikatory uwzględniające wielkość liter w standardach takich jak XML i HTTP
    • Ustawienia bezpieczeństwa uwzględniające wielkość liter
  • OrdinalIgnoreCase
    • Wewnętrzne identyfikatory bez rozróżniania wielkości liter
    • Identyfikatory bez rozróżniania wielkości liter w standardach takich jak XML i HTTP
    • Ścieżki plików (w systemie Microsoft Windows)
    • Klucze / wartości rejestru
    • Zmienne środowiska
    • Identyfikatory zasobów (na przykład nazwy uchwytów)
    • Ustawienia bezpieczeństwa bez rozróżniania wielkości liter
  • InvariantCulture lub InvariantCultureIgnoreCase
    • Niektóre utrwalały dane istotne pod względem językowym
    • Wyświetlanie danych językowych wymagających stałej kolejności sortowania
  • CurrentCulture lub CurrentCultureIgnoreCase
    • Dane wyświetlane użytkownikowi
    • Większość danych wejściowych użytkownika

Uwaga: Wyliczenie StringComparison, a także przeciążenia metod porównywania ciągów, istnieją od wersji .NET 2.0.


String.CompareTo Method (String)

Jest w rzeczywistości bezpieczną implementacją metody IComparable.CompareTo . Domyślna interpretacja: CurrentCulture.

Stosowanie:

Metoda CompareTo została zaprojektowana przede wszystkim do stosowania w operacjach sortowania lub alfabetycznego

A zatem

Wdrożenie interfejsu IComparable będzie koniecznie korzystało z tej metody

Metoda String.Compare

Statyczny element klasy String, który ma wiele przeciążeń. Domyślna interpretacja: CurrentCulture.

O ile to możliwe, należy wywołać przeciążenie metody Porównaj, która zawiera parametr StringComparison.

Metoda String.Equals

Przesłonięty z klasy Object i przeciążony dla bezpieczeństwa typu. Domyślna interpretacja: porządkowa. Zauważ, że:

Metody równości klasy String obejmują static Equals , operator static == oraz metodę instancji Equals .


Klasa StringComparer

Istnieje również inny sposób radzenia sobie z porównaniami ciągów, w szczególności w celu sortowania:

Za pomocą klasy StringComparer można utworzyć porównanie specyficzne dla typu w celu posortowania elementów w ogólnej kolekcji. Klasy takie jak Hashtable, Dictionary, SortedList i SortedList używają klasy StringComparer do celów sortowania.


2
Według niektórych innych postów na SO, wszystkie metody inne niż porządkowe mają przypadki, w których Porównaj (a, b) i Porównaj (b, a) mogą zwrócić 1, a błąd został sklasyfikowany jako „nie zostanie naprawiony „. Jako taki, nie jestem pewien, czy takie porównania mają jakikolwiek przypadek użycia.
supercat

@ superupat możesz to podlinkować lub podać przykład?
Noctis

1
Zobacz stackoverflow.com/questions/17599084/... omówienie tej kwestii.
supercat

7

Nie chodzi o to, że wydajność zwykle ma znaczenie w 99% przypadków, gdy musisz to zrobić, ale jeśli musiałbyś to zrobić kilka milionów razy w pętli, wysoce sugerowałbym, abyś użył .Equals lub ==, ponieważ jak tylko znajdzie postać to nie pasuje, wyrzuca to wszystko jako fałszywe, ale jeśli użyjesz CompareTo, będzie musiał dowiedzieć się, która postać jest mniejsza od drugiej, co prowadzi do nieco gorszego czasu wydajności.

Jeśli Twoja aplikacja będzie działać w różnych krajach, radzę przyjrzeć się implikacjom CultureInfo i ewentualnie użyć .Equals. Ponieważ tak naprawdę piszę aplikacje tylko dla USA (i nie obchodzi mnie, czy ktoś nie działa poprawnie), zawsze używam ==.


5

W wymienionych tutaj formularzach nie ma między nimi dużej różnicy. CompareTokończy się wywołaniem CompareInfometody, która dokonuje porównania przy użyciu bieżącej kultury; Equalsjest wywoływany przez ==operatora.

Jeśli weźmiesz pod uwagę przeciążenia, wtedy sprawy będą się różnić. Comparei ==może użyć bieżącej kultury tylko do porównania łańcucha. Equalsi String.Comparemoże przyjąć StringComparisonargument wyliczenia, który pozwala określić porównanie bez rozróżniania kultur lub bez rozróżniania wielkości liter. Tylko String.Comparepozwala na podanie CultureInfoi przeprowadzenia porównań przy użyciu kulturę, inny niż domyślny kultury.

Ze względu na jego wszechstronność używam String.Comparewięcej niż jakiejkolwiek innej metody porównania; pozwala mi dokładnie określić, czego chcę.


2

Jedną z DUŻYCH różnic, na które należy zwrócić uwagę, jest to, że .Equals () wyrzuci wyjątek, jeśli pierwszy ciąg jest pusty, podczas gdy == nie.

       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): NIE używaj, jeśli głównym celem jest ustalenie, czy dwa łańcuchy są równoważne
  • s1 == s2: Nie można zignorować wielkości liter
  • s1.Equals (s2, StringComparison): Zgłasza wyjątek NullReferenceException, jeśli s1 ma wartość null
  • String.Equals (s2, StringComparison): W procesie eliminacjistatyczną metodą jest WINNER (zakładając typowy przypadek użycia w celu ustalenia, czy dwa ciągi są równoważne)!

-1

Korzystanie z .Equals jest również znacznie łatwiejsze do odczytania .


-9

z .Equals zyskujesz także opcje StringComparison. bardzo przydatny do ignorowania sprawy i innych rzeczy.

btw, to da wynik fałszywy

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

return a==b

Ponieważ == porównuje wartości aib (które są wskaźnikami), będzie to miało wartość prawdy tylko wtedy, gdy wskaźniki wskażą ten sam obiekt w pamięci. .Equals dereferentuje wskaźniki i porównuje wartości zapisane w wskaźnikach. a. Równości (b) byłyby tutaj prawdziwe.

a jeśli zmienisz b na:

b = "MYSTRING";

wtedy a. Równania (b) są fałszywe, ale

a.Equals(b, StringComparison.OrdinalIgnoreCase) 

byłoby prawdą

a.CompareTo (b) wywołuje funkcję CompareTo ciągu, która porównuje wartości wskaźników i zwraca <0, jeśli wartość przechowywana w a jest mniejsza niż wartość przechowywana w b, zwraca 0, jeśli a. Equals (b) jest prawdą, i > 0 w przeciwnym razie. Jednak w tym rozróżniana jest wielkość liter, myślę, że są możliwe opcje, aby CompareTo zignorować wielkość liter i tak dalej, ale nie mam czasu, aby się teraz przyjrzeć. Jak już inni stwierdzili, można to zrobić w celu sortowania. Porównywanie równości w ten sposób spowodowałoby niepotrzebne koszty ogólne.

Jestem pewien, że pomijam pewne rzeczy, ale myślę, że powinno to wystarczyć do rozpoczęcia eksperymentów, jeśli potrzebujesz więcej szczegółów.


9
Część a == b jest niepoprawna. Operator == jest skutecznie przeciążony dla klasy String i porównuje wartości niezależnie od faktycznych odniesień.
Goyuix
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.