Differenze nei metodi di confronto delle stringhe in C #


261

Il confronto delle stringhe in C # è piuttosto semplice. In effetti ci sono diversi modi per farlo. Ne ho elencati alcuni nel blocco di seguito. Ciò di cui sono curioso sono le differenze tra loro e quando uno dovrebbe essere usato rispetto agli altri? Si dovrebbe evitare a tutti i costi? Ce ne sono altri che non ho elencato?

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

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

(Nota: in questo esempio cerco l'uguaglianza, non inferiore o maggiore di, ma mi sento libero di commentare anche questo)


4
Una trappola è che non puoi fare stringValue.Equals (null) in quanto ciò presuppone che puoi chiamare un metodo su null
johnc


@RobertHarvey Il motivo per cui vengo a StackOverflow è che non devo leggere più pagine per le risposte.
Syaiful Nizam Yahya,

@Syaiful: Il motivo per cui vengo a Stack Overflow è trovare risposte che non sono nella documentazione.
Robert Harvey,

Risposte:


231

Ecco le regole per il funzionamento di queste funzioni:

stringValue.CompareTo(otherStringValue)

  1. null viene prima di una stringa
  2. utilizza CultureInfo.CurrentCulture.CompareInfo.Compare, il che significa che utilizzerà un confronto dipendente dalla cultura. Ciò potrebbe significare che ßsarà comparabile SSin Germania, o simile

stringValue.Equals(otherStringValue)

  1. null non è considerato uguale a niente
  2. a meno che non specifichi StringComparisonun'opzione, userà quello che sembra un controllo di uguaglianza ordinale diretto, cioè ßnon è lo stesso di SS, in qualsiasi lingua o cultura

stringValue == otherStringValue

  1. Non è lo stesso di stringValue.Equals().
  2. L' ==operatore chiama il Equals(string a, string b)metodo statico (che a sua volta passa a un interno EqualsHelperper eseguire il confronto.
  3. Chiamare .Equals()una nullstringa ottiene nullun'eccezione di riferimento, mentre on ==non lo è.

Object.ReferenceEquals(stringValue, otherStringValue)

Verifica solo che i riferimenti siano gli stessi, ovvero che non siano solo due stringhe con lo stesso contenuto, ma stai confrontando un oggetto stringa con se stesso.


Si noti che con le opzioni sopra che usano le chiamate di metodo, ci sono sovraccarichi con più opzioni per specificare come confrontare.

Il mio consiglio se si desidera solo verificare l'uguaglianza è decidere se si desidera utilizzare o meno un confronto dipendente dalla cultura, quindi utilizzare .CompareToo .Equals, a seconda della scelta.


5
"stringValue.Equals (otherStringValue): null non è uguale a null" Lol, direi di no. null è uguale all'eccezione ObjectReferenceNotSet.
Kevin,

29
== non è lo stesso di .Equals () ... L'operatore == chiama il metodo statico Equals (stringa a, stringa b) (che a sua volta passa a un EqualsHelper interno per fare il confronto. Chiamare .Equals su null stringa ottiene riferimento null escl., mentre on == no.
Dan C.

2
D'altra parte, .Equals è leggermente più veloce (un metodo in meno chiama internamente), ma meno leggibile - probabilmente, ovviamente :).
Dan C.

Pensavo che '==' farà confronti di riferimento e object.equals farà confronti di valore. In che modo '==' e string.equals funzionano allo stesso modo?
dal

@ LasseV.Karlsen Su cosa pensi String.Compare?
JDandChips,

72

Da MSDN:

"Il metodo CompareTo è stato progettato principalmente per l'uso in operazioni di ordinamento o alfabetizzazione. Non dovrebbe essere utilizzato quando lo scopo principale della chiamata al metodo è determinare se due stringhe sono equivalenti. Per determinare se due stringhe sono equivalenti, chiamare il metodo Equals. "

Suggeriscono di usare .Equalsinvece di .CompareTocercare solo l'uguaglianza. Non sono sicuro che ci sia una differenza tra .Equalse ==per la stringclasse. A volte userò .Equalso Object.ReferenceEqualsinvece che ==per le mie classi nel caso in cui qualcuno si presenti in un secondo momento e ridefinisca l' ==operatore per quella classe.


18
Ti è mai successo? (Ridefinendo ==) ... Lo vedo come una programmazione troppo difensiva =)
juan

Sì, ecco perché ora uso Object.ReferenceEquals quando cerco l'uguaglianza degli oggetti :). Potrebbe essere un po 'troppo difensivo, ma non sono maniacale al riguardo e in verità questa situazione non si presenta molto spesso.
Ed S.

Dubito che questa "codifica difensiva" sia utile. Cosa succede se il proprietario della classe deve sovrascrivere l'operatore ==, quindi scopre che nessuno lo sta utilizzando?
Dave Van den Eynde,

1
@DaveVandenEynde: Sì ... l'ho scritto qualche tempo fa. Non lo faccio regolarmente, solo scavalcando. Equivalenti quando appropriato.
Ed S.

1
La raccomandazione di Microsoft è registrata qui: Best practice per l'utilizzo delle stringhe in .NET Framework
JJS

50

Se sei sempre curioso delle differenze nei metodi BCL, Reflector è tuo amico :-)

Seguo queste linee guida:

Corrispondenza esatta: EDIT: in precedenza avevo sempre utilizzato l'operatore == in base al principio che all'interno di Equals (stringa, stringa) l'operatore object == viene utilizzato per confrontare i riferimenti dell'oggetto ma sembra strA.Equals (strB) è ancora 1-11% più veloce complessivo di string.Equals (strA, strB), strA == strB e string.CompareOrdinal (strA, strB). Ho testato in loop con un StopWatch su valori di stringa sia interni / non interni, con lunghezze di stringa uguali / diverse e dimensioni variabili (da 1B a 5 MB).

strA.Equals(strB)

Partita leggibile dall'uomo (culture occidentali, senza distinzione tra maiuscole e minuscole):

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

Corrispondenza leggibile dall'uomo (Tutte le altre culture, maiuscole / minuscole / accento / kana / ecc definite da CultureInfo):

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

Partita leggibile dall'uomo con regole personalizzate (tutte le altre culture):

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

18

Come Ed detto , CompareTo viene utilizzato per l'ordinamento.

C'è una differenza, tuttavia, tra .Equals e ==.

== risolve essenzialmente il seguente codice:

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

Il semplice motivo è il seguente genererà un'eccezione:

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

bool equal = a.Equals(b);

E quanto segue non:

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

bool equal = a == b;

15

Buone spiegazioni e pratiche sui problemi di confronto delle stringhe sono disponibili nell'articolo Nuovi consigli per l'utilizzo delle stringhe in Microsoft .NET 2.0 e anche nelle migliori pratiche per l'utilizzo delle stringhe in .NET Framework .


Ciascuno dei metodi citati (e altri) ha uno scopo particolare. La differenza fondamentale tra loro è che tipo di enumerazione StringComparison stanno usando per impostazione predefinita. Esistono diverse opzioni:

  • CurrentCulture
  • CurrentCultureIgnoreCase
  • InvariantCulture
  • InvariantCultureIgnoreCase
  • Ordinale
  • OrdinalIgnoreCase

Ciascuno dei precedenti tipi di confronto ha come target diversi casi d'uso:

  • Ordinale
    • Identificatori interni con distinzione tra maiuscole e minuscole
    • Identificatori sensibili al maiuscolo / minuscolo in standard come XML e HTTP
    • Impostazioni relative alla sicurezza con distinzione tra maiuscole e minuscole
  • OrdinalIgnoreCase
    • Identificatori interni senza distinzione tra maiuscole e minuscole
    • Identificatori senza distinzione tra maiuscole e minuscole in standard come XML e HTTP
    • Percorsi dei file (su Microsoft Windows)
    • Chiavi / valori del registro
    • Variabili ambientali
    • Identificatori di risorse (gestire i nomi, ad esempio)
    • Impostazioni relative alla sicurezza senza distinzione tra maiuscole e minuscole
  • InvariantCulture o InvariantCultureIgnoreCase
    • Alcuni dati linguisticamente rilevanti persistono
    • Visualizzazione di dati linguistici che richiedono un ordinamento fisso
  • CurrentCulture o CurrentCultureIgnoreCase
    • Dati visualizzati per l'utente
    • La maggior parte degli input dell'utente

Si noti che l' enumerazione StringComparison e i sovraccarichi per i metodi di confronto delle stringhe esistono da .NET 2.0.


Metodo String.CompareTo (String)

È in realtà l'implementazione sicura del metodo IComparable.CompareTo Method . Interpretazione predefinita: CurrentCulture.

Uso:

Il metodo CompareTo è stato progettato principalmente per l'uso in operazioni di ordinamento o alfabetizzazione

così

L'implementazione dell'interfaccia IComparable utilizzerà necessariamente questo metodo

Metodo String.Compare

Un membro statico della classe String che presenta molti sovraccarichi. Interpretazione predefinita: CurrentCulture.

Quando possibile, è necessario chiamare un sovraccarico del metodo Compare che include un parametro StringComparison.

Metodo String.Equals

Sostituito dalla classe Object e sovraccaricato per la sicurezza del tipo. Interpretazione predefinita: ordinale. Notare che:

I metodi di uguaglianza della classe String includono gli uguali statici , l' operatore statico == e il metodo di istanza uguale .


Classe StringComparer

Esiste anche un altro modo di gestire i confronti di stringhe in particolare per l'ordinamento:

È possibile utilizzare la classe StringComparer per creare un confronto specifico del tipo per ordinare gli elementi in una raccolta generica. Classi come Hashtable, Dictionary, SortedList e SortedList utilizzano la classe StringComparer per scopi di ordinamento.


2
Secondo alcuni altri post su SO, tutti i metodi diversi da quelli ordinali hanno casi in cui Compare (a, b) e Compare (b, a) possono entrambi restituire 1 e il bug è stato classificato come "non sarà risolto ". Pertanto, non sono sicuro che tali confronti abbiano alcun caso d'uso.
supercat

@supercat puoi collegarti a questo o fare un esempio?
Noctis,

1
Vedi stackoverflow.com/questions/17599084/… per una discussione del problema.
supercat

7

Non che le prestazioni contino di solito con il 99% delle volte in cui è necessario, ma se dovessi farlo in un ciclo diverse milioni di volte, ti consiglio vivamente di utilizzare .Equals o == perché non appena trova un personaggio che non corrisponde, il tutto risulta falso, ma se si utilizza CompareTo dovrà capire quale personaggio è meno dell'altro, portando a tempi di esecuzione leggermente peggiori.

Se la tua app verrà eseguita in diversi paesi, ti consiglio di dare un'occhiata alle implicazioni di CultureInfo e possibilmente utilizzare .Equals. Dal momento che scrivo app solo per gli Stati Uniti (e non mi interessa se non funziona correttamente da qualcuno), uso sempre ==.


5

Nelle forme che hai elencato qui, non c'è molta differenza tra i due. CompareTofinisce per chiamare un CompareInfometodo che fa un confronto usando la cultura attuale; Equalsviene chiamato dal== dall'operatore.

Se consideri i sovraccarichi, le cose si differenziano. Comparee ==può usare solo la cultura corrente per confrontare una stringa. Equalse String.Comparepuò prendere un StringComparisonargomento di enumerazione che ti consente di specificare confronti insensibili alla cultura o al maiuscolo / minuscolo. String.CompareTi permette solo di specificare aCultureInfo ed eseguire confronti utilizzando una cultura diversa da quella predefinita.

A causa della sua versatilità, trovo di usare String.Comparepiù di qualsiasi altro metodo di confronto; mi permette di specificare esattamente quello che voglio.


2

Una GRANDE differenza da notare è .Equals () genererà un'eccezione se la prima stringa è nulla, mentre == no.

       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): NON utilizzare se lo scopo principale è determinare se due stringhe sono equivalenti
  • s1 == s2: impossibile ignorare il caso
  • s1.Equals (s2, StringComparison): genera NullReferenceException se s1 è null
  • String.Equals (s2, StringComparison): in base al processo di eliminazione, questo metodo statico è il VINCITORE (presupponendo un caso d'uso tipico per determinare se due stringhe sono equivalenti)!

-1

L'uso di .Equals è anche molto più facile da leggere .


-9

con .Equals, ottieni anche le opzioni StringComparison. molto utile per ignorare il caso e altre cose.

a proposito, questo verrà valutato come falso

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

return a==b

Poiché == confronta i valori di aeb (che sono puntatori), questo valuterà su vero solo se i puntatori puntano allo stesso oggetto in memoria. .Equals dereferenzia i puntatori e confronta i valori memorizzati sui puntatori. a.Equals (b) sarebbe vero qui.

e se cambi b in:

b = "MYSTRING";

quindi a.Equals (b) è falso, ma

a.Equals(b, StringComparison.OrdinalIgnoreCase) 

sarebbe vero

a.CompareTo (b) chiama la funzione CompareTo della stringa che confronta i valori sui puntatori e restituisce <0 se il valore memorizzato in a è inferiore al valore memorizzato in b, restituisce 0 se a.Equals (b) è vero e > 0 altrimenti. Tuttavia, questo fa distinzione tra maiuscole e minuscole, penso che ci siano probabilmente opzioni per CompareTo per ignorare il caso e simili, ma non ho tempo di guardare ora. Come altri hanno già affermato, questo sarebbe fatto per l'ordinamento. Il confronto per l'uguaglianza in questo modo comporterebbe spese generali non necessarie.

Sono sicuro che tralascerò le cose, ma penso che dovrebbero essere sufficienti informazioni per iniziare a sperimentare se hai bisogno di maggiori dettagli.


9
La parte a == b non è corretta. L'operatore == è effettivamente sovraccaricato per la classe String e confronta i valori indipendentemente dai riferimenti effettivi.
Goyuix,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.