Différences dans les méthodes de comparaison de chaînes en C #


261

La comparaison de chaînes en C # est assez simple. En fait, il existe plusieurs façons de procéder. J'en ai énuméré quelques-uns dans le bloc ci-dessous. Ce qui m'intéresse, ce sont les différences entre eux et quand l'un devrait être utilisé par rapport aux autres? Faut-il à tout prix éviter? Y en a-t-il d'autres que je n'ai pas répertoriés?

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

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

(Remarque: je recherche l'égalité dans cet exemple, pas moins ou plus que mais n'hésitez pas à commenter cela également)


4
Un piège est que vous ne pouvez pas faire stringValue.Equals (null) car cela suppose que vous pouvez appeler une méthode sur null
johnc


@RobertHarvey La raison pour laquelle je viens à stackoverflow est que je n'ai pas à lire plusieurs pages pour obtenir des réponses.
Syaiful Nizam Yahya

@Syaiful: La raison pour laquelle je viens à Stack Overflow est de trouver des réponses qui ne sont pas dans la documentation.
Robert Harvey

Réponses:


231

Voici les règles de fonctionnement de ces fonctions:

stringValue.CompareTo(otherStringValue)

  1. null vient avant une chaîne
  2. il utilise CultureInfo.CurrentCulture.CompareInfo.Compare, ce qui signifie qu'il utilisera une comparaison dépendante de la culture. Cela pourrait signifier que ßsera comparable à SSen Allemagne, ou similaire

stringValue.Equals(otherStringValue)

  1. null n'est pas considéré comme égal à quoi que ce soit
  2. sauf si vous spécifiez une StringComparisonoption, il utilisera ce qui ressemble à une vérification directe de l'égalité ordinale, c'est ß-à- dire qui n'est pas la même que SSdans n'importe quelle langue ou culture

stringValue == otherStringValue

  1. N'est pas le même que stringValue.Equals().
  2. L' ==opérateur appelle la Equals(string a, string b)méthode statique (qui à son tour va à un interne EqualsHelperpour faire la comparaison.
  3. L'appel .Equals()sur une nullchaîne obtient une nullexception de référence, ==contrairement à on.

Object.ReferenceEquals(stringValue, otherStringValue)

Vérifie simplement que les références sont les mêmes, c'est-à-dire qu'il ne s'agit pas seulement de deux chaînes avec le même contenu, vous comparez un objet chaîne avec lui-même.


Notez qu'avec les options ci-dessus qui utilisent des appels de méthode, il y a des surcharges avec plus d'options pour spécifier comment comparer.

Mon conseil si vous voulez simplement vérifier l'égalité est de décider si vous voulez utiliser une comparaison dépendante de la culture ou non, puis utilisez .CompareToou .Equals, selon le choix.


5
"stringValue.Equals (otherStringValue): null n'est pas égal à null" Lol, je dirais que non. null est égal à l'exception ObjectReferenceNotSet.
Kevin

29
== n'est pas la même chose que .Equals () ... L'opérateur == appelle la méthode statique Equals (chaîne a, chaîne b) (qui à son tour va à un EqualsHelper interne pour faire la comparaison. Appel de .Equals sur un null chaîne obtient une référence nulle exc., tandis que sur == ne le fait pas
Dan C.

2
D'un autre côté, .Equals est légèrement plus rapide (un appel de méthode en moins en interne), mais moins lisible - sans doute, bien sûr :).
Dan C.

Je pensais que '==' ferait des comparaisons de référence et object.equals ferait des comparaisons de valeur.Comment '==' et string.equals fonctionnent de la même manière?
amesh

@ LasseV.Karlsen Quelle est votre opinion String.Compare?
JDandChips

72

Depuis MSDN:

"La méthode CompareTo a été conçue principalement pour être utilisée dans les opérations de tri ou d'alphabétisation. Elle ne doit pas être utilisée lorsque l'objectif principal de l'appel de méthode est de déterminer si deux chaînes sont équivalentes. Pour déterminer si deux chaînes sont équivalentes, appelez la méthode Equals. "

Ils suggèrent d'utiliser .Equalsplutôt que de .CompareTorechercher uniquement l'égalité. Je ne sais pas s'il y a une différence entre .Equalset ==pour la stringclasse. J'utiliserai parfois .Equalsou Object.ReferenceEqualsau lieu de ==pour mes propres classes au cas où quelqu'un viendrait plus tard et redéfinirait l' ==opérateur pour cette classe.


18
Cela vous est-il déjà arrivé? (Redéfinir ==) ... Je le vois comme une programmation trop défensive waaaay =)
juan

Oui, c'est pourquoi j'utilise maintenant Object.ReferenceEquals lorsque je recherche l'égalité d'objet :). C'est peut-être un peu trop défensif, mais je ne suis pas maniaque à ce sujet et, à vrai dire, cette situation n'apparaît pas très souvent.
Ed S.

Je doute que ce «codage défensif» soit utile. Que faire si le propriétaire de la classe doit remplacer l'opérateur ==, puis découvre que personne ne l'utilise?
Dave Van den Eynde

1
@DaveVandenEynde: Ouais ... Je l'ai écrit il y a quelque temps. Je ne le fais pas régulièrement, je ne remplace que les égalités, le cas échéant.
Ed S.

1
La recommandation de Microsoft est enregistrée ici: Meilleures pratiques pour l'utilisation de chaînes dans le .NET Framework
JJS

50

Si vous êtes curieux de connaître les différences dans les méthodes BCL, Reflector est votre ami :-)

Je suis ces directives:

Correspondance exacte: EDIT: j'ai toujours utilisé l'opérateur == sur le principe que dans Equals (chaîne, chaîne), l'opérateur objet == est utilisé pour comparer les références d'objet, mais il semble que strA.Equals (strB) soit toujours de 1 à 11%. globalement plus rapide que string.Equals (strA, strB), strA == strB et string.CompareOrdinal (strA, strB). J'ai testé en boucle avec un chronomètre sur les deux valeurs de chaîne internes / non internes, avec des longueurs de chaîne identiques / différentes et des tailles variables (1B à 5MB).

strA.Equals(strB)

Correspondance lisible par l'homme (cultures occidentales, insensible à la casse):

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

Correspondance lisible par l'homme (toutes les autres cultures, cas / accent / kana / etc insensible défini par CultureInfo):

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

Correspondance lisible par l'homme avec des règles personnalisées (toutes les autres cultures):

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

18

Comme l'a dit Ed , CompareTo est utilisé pour le tri.

Il existe cependant une différence entre .Equals et ==.

== résout essentiellement le code suivant:

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

La raison simple est que ce qui suit lèvera une exception:

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

bool equal = a.Equals(b);

Et ce qui suit ne sera pas:

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

bool equal = a == b;

15

De bonnes explications et pratiques sur les problèmes de comparaison de chaînes peuvent être trouvées dans l'article Nouvelles recommandations pour l'utilisation de chaînes dans Microsoft .NET 2.0 et également dans Meilleures pratiques pour l'utilisation de chaînes dans .NET Framework .


Chacune des méthodes mentionnées (et d'autres) a un objectif particulier. La principale différence entre eux est le type d' énumération StringComparison qu'ils utilisent par défaut. Il existe plusieurs options:

  • CurrentCulture
  • CurrentCultureIgnoreCase
  • InvariantCulture
  • InvariantCultureIgnoreCase
  • Ordinal
  • OrdinalIgnoreCase

Chacun des types de comparaison ci-dessus cible un cas d'utilisation différent:

  • Ordinal
    • Identificateurs internes sensibles à la casse
    • Identificateurs sensibles à la casse dans des normes comme XML et HTTP
    • Paramètres liés à la sécurité sensibles à la casse
  • OrdinalIgnoreCase
    • Identifiants internes insensibles à la casse
    • Identificateurs insensibles à la casse dans des normes comme XML et HTTP
    • Chemins de fichiers (sous Microsoft Windows)
    • Clés / valeurs de registre
    • Variables d'environnement
    • Identificateurs de ressources (noms de descripteurs, par exemple)
    • Paramètres liés à la sécurité insensible à la casse
  • InvariantCulture ou InvariantCultureIgnoreCase
    • Certaines données persistantes pertinentes sur le plan linguistique
    • Affichage des données linguistiques nécessitant un ordre de tri fixe
  • CurrentCulture ou CurrentCultureIgnoreCase
    • Données affichées à l'utilisateur
    • La plupart des entrées utilisateur

Notez que l' énumération StringComparison ainsi que les surcharges pour les méthodes de comparaison de chaînes existent depuis .NET 2.0.


String.CompareTo, méthode (String)

Est en fait une implémentation sûre de la méthode IComparable.CompareTo . Interprétation par défaut: CurrentCulture.

Usage:

La méthode CompareTo a été conçue principalement pour être utilisée dans les opérations de tri ou d’alphabétisation

Donc

L'implémentation de l'interface IComparable utilisera nécessairement cette méthode

String.Compare, méthode

Un membre statique de String Class qui a de nombreuses surcharges. Interprétation par défaut: CurrentCulture.

Dans la mesure du possible, vous devez appeler une surcharge de la méthode Compare qui inclut un paramètre StringComparison.

String.Equals, méthode

Overriden de la classe Object et surchargé pour la sécurité du type. Interprétation par défaut: Ordinal. Remarquerez que:

Les méthodes d'égalité de la classe String incluent les statiques Equals , l' opérateur statique == et la méthode d'instance Equals .


Classe StringComparer

Il existe également une autre façon de gérer les comparaisons de chaînes, notamment le tri:

Vous pouvez utiliser la classe StringComparer pour créer une comparaison spécifique au type pour trier les éléments dans une collection générique. Des classes telles que Hashtable, Dictionary, SortedList et SortedList utilisent la classe StringComparer à des fins de tri.


2
Selon certains autres articles sur SO, toutes les méthodes autres que celles ordinales ont des cas où Compare (a, b) et Compare (b, a) peuvent tous deux renvoyer 1, et le bogue a été classé comme "ne sera pas corrigé" ". En tant que tel, je ne suis pas sûr que de telles comparaisons aient un cas d'utilisation.
supercat

@supercat pouvez-vous créer un lien vers cela ou donner un exemple?
Noctis

1
Voir stackoverflow.com/questions/17599084/… pour une discussion du problème.
supercat

7

Ce n'est pas que les performances comptent généralement avec 99% des fois où vous devez le faire, mais si vous deviez le faire en boucle plusieurs millions de fois, je vous suggère fortement d'utiliser .Equals ou == car dès qu'il trouve un personnage cela ne correspond pas, cela jette le tout comme faux, mais si vous utilisez CompareTo, il devra déterminer quel caractère est inférieur à l'autre, ce qui entraîne un temps de performance légèrement pire.

Si votre application s'exécute dans différents pays, je vous recommande de jeter un coup d'œil aux implications de CultureInfo et éventuellement d'utiliser .Equals. Comme je n'écris vraiment que des applications pour les États-Unis (et je me fiche que cela ne fonctionne pas correctement pour quelqu'un), j'utilise toujours ==.


5

Dans les formulaires que vous avez énumérés ici, il n'y a pas beaucoup de différence entre les deux. CompareTofinit par appeler une CompareInfométhode qui fait une comparaison en utilisant la culture actuelle; Equalsest appelé par l' ==opérateur.

Si vous envisagez des surcharges, les choses deviennent différentes. Compareet ==ne peut utiliser la culture actuelle que pour comparer une chaîne. Equalset String.Comparepeut prendre un StringComparisonargument d'énumération qui vous permet de spécifier des comparaisons insensibles à la culture ou à la casse. Vous String.Comparepermet uniquement de spécifier CultureInfoet d'effectuer des comparaisons à l'aide d'une culture autre que la culture par défaut.

En raison de sa polyvalence, je trouve que j'utilise String.Compareplus que toute autre méthode de comparaison; cela me permet de spécifier exactement ce que je veux.


2

Une GRANDE différence à noter est que .Equals () lèvera une exception si la première chaîne est nulle, alors que == ne le fera pas.

       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): NE PAS utiliser si l'objectif principal est de déterminer si deux chaînes sont équivalentes
  • s1 == s2: Impossible d'ignorer la casse
  • s1.Equals (s2, StringComparison): lève NullReferenceException si s1 est nul
  • String.Equals (s2, StringComparison): Par processus d'élimination, cette méthode statique est le GAGNANT (en supposant un cas d'utilisation typique pour déterminer si deux chaînes sont équivalentes)!

-1

L'utilisation de .Equals est également beaucoup plus facile à lire .


-9

avec .Equals, vous bénéficiez également des options StringComparison. très pratique pour ignorer la casse et d'autres choses.

btw, ce sera faux

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

return a==b

Puisque == compare les valeurs de a et b (qui sont des pointeurs), cela ne sera évalué à vrai que si les pointeurs pointent vers le même objet en mémoire. .Equals déréférence les pointeurs et compare les valeurs stockées au niveau des pointeurs. a. Les égalités (b) seraient vraies ici.

et si vous changez b en:

b = "MYSTRING";

alors a.Equals (b) est faux, mais

a.Equals(b, StringComparison.OrdinalIgnoreCase) 

serait vrai

a.CompareTo (b) appelle la fonction CompareTo de la chaîne qui compare les valeurs aux pointeurs et renvoie <0 si la valeur stockée à a est inférieure à la valeur stockée à b, renvoie 0 si a.Equals (b) est vraie, et > 0 sinon. Cependant, ceci est sensible à la casse, je pense qu'il existe probablement des options pour CompareTo pour ignorer la casse et autres, mais n'avons pas le temps de regarder maintenant. Comme d'autres l'ont déjà dit, cela se ferait pour le tri. La comparaison de l'égalité de cette manière entraînerait des frais généraux inutiles.

Je suis sûr que je laisse de côté, mais je pense que cela devrait être assez d'informations pour commencer à expérimenter si vous avez besoin de plus de détails.


9
La partie a == b est incorrecte. L'opérateur == est effectivement surchargé pour la classe String et il compare les valeurs quelles que soient les références réelles.
Goyuix
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.