Ssortingng.Equals () et l’opérateur == sont-ils vraiment identiques?

Sont-ils vraiment les mêmes? Aujourd’hui, j’ai rencontré ce problème. Voici le vidage de la fenêtre immédiate:

?s "Category" ?tvi.Header "Category" ?s == tvi.Header false ?s.Equals(tvi.Header) true ?s == tvi.Header.ToSsortingng() true 

Donc, s et tvi.Header contiennent “Category”, mais == renvoie false et Equals() renvoie true.

s est défini en tant que chaîne, tvi.Header est en réalité un WPF TreeViewItem.Header . Alors, pourquoi retournent-ils des résultats différents? J’ai toujours pensé qu’ils étaient interchangeables en C #.

Quelqu’un peut-il expliquer pourquoi c’est?

Deux différences:

  • Equals est polymorphe (c.-à-d. Qu’il peut être remplacé, et l’implémentation utilisée dépendra du type de temps d’exécution de l’object cible), tandis que l’implémentation de == utilisée est déterminée en fonction des types de compilation des objects:

     // Avoid getting confused by interning object x = new SsortingngBuilder("hello").ToSsortingng(); object y = new SsortingngBuilder("hello").ToSsortingng(); if (x.Equals(y)) // Yes // The comstackr doesn't know to call ==(ssortingng, ssortingng) so it generates // a reference comparision instead if (x == y) // No ssortingng xs = (ssortingng) x; ssortingng ys = (ssortingng) y; // Now *this* will call ==(ssortingng, ssortingng), comparing values appropriately if (xs == ys) // Yes 
  • Equals va bash si vous l’appelez sur null, == ne sera pas

     ssortingng x = null; ssortingng y = null; if (x.Equals(y)) // Bang if (x == y) // Yes 

Notez que vous pouvez éviter que ce dernier ne soit un problème avec object.Equals :

 if (object.Equals(x, y)) // Fine even if x or y is null 

Les contradictions apparentes qui apparaissent dans la question sont dues au fait que, dans un cas, la fonction Equals est appelée sur un object ssortingng et dans l’autre cas, l’opérateur == est appelé sur le type System.Object . ssortingng et object implémentent l’égalité différemment les uns des autres (valeur et référence respectivement).

Au-delà de cela, tout type peut définir == et Equals différemment, donc en général ils ne sont pas interchangeables.

Voici un exemple utilisant double (de la note de Joseph Albahari au §7.9.2 de la spécification du langage C #):

 double x = double.NaN; Console.WriteLine (x == x); // False Console.WriteLine (x != x); // True Console.WriteLine (x.Equals(x)); // True 

Il poursuit en disant que la double.Equals(double) été conçue pour fonctionner correctement avec les listes et les dictionnaires. L’opérateur == , d’autre part, a été conçu pour suivre la norme IEEE 754 pour les types à virgule flottante.

Dans le cas spécifique de la détermination de l’égalité des chaînes, la préférence de l’indussortinge consiste à n’utiliser ni == ni ssortingng.Equals(ssortingng) plupart du temps. Ces méthodes déterminent si deux chaînes ont le même caractère pour le caractère, ce qui est rarement le comportement correct. Il est préférable d’utiliser ssortingng.Equals(ssortingng, SsortingngComparison) , qui vous permet de spécifier un type de comparaison particulier. En utilisant la comparaison correcte, vous pouvez éviter beaucoup de bogues potentiels (très difficiles à diagnostiquer).

Voici un exemple:

 ssortingng one = "Caf\u00e9"; // U+00E9 LATIN SMALL LETTER E WITH ACUTE ssortingng two = "Cafe\u0301"; // U+0301 COMBINING ACUTE ACCENT Console.WriteLine(one == two); // False Console.WriteLine(one.Equals(two)); // False Console.WriteLine(one.Equals(two, SsortingngComparison.InvariantCulture)); // True 

Les deux chaînes de cet exemple se ressemblent (“Café”), ce qui peut être très difficile à déboguer si vous utilisez une égalité (ordinale) naïve.

C # a deux concepts “égaux”: Equals et ReferenceEquals . Pour la plupart des classes que vous rencontrerez, l’opérateur == utilise l’un ou l’autre (ou les deux), et ne teste généralement que des ReferenceEquals lorsqu’il traite des types de référence (mais la classe ssortingng est une instance où C # sait déjà tester l’égalité des valeurs) .

  • Equals compare les valeurs. (Même si deux variables int distinctes n’existent pas au même endroit en mémoire, elles peuvent toujours contenir la même valeur.)
  • ReferenceEquals compare la référence et indique si les opérandes désignent le même object en mémoire.

Exemple de code:

 var s1 = new SsortingngBuilder("str"); var s2 = new SsortingngBuilder("str"); SsortingngBuilder sNull = null; s1.Equals(s2); // True object.ReferenceEquals(s1, s2); // False s1 == s2 // True - it calls Equals within operator overload s1 == sNull // False object.ReferenceEquals(s1, sNull); // False s1.Equals(sNull); // Nono! Explode (Exception) 

La propriété Header de TreeViewItem est typée statiquement pour être de type object .

Par conséquent, le == donne un false . Vous pouvez reproduire cela avec l’extrait simple suivant:

 object s1 = "Hallo"; // don't use a ssortingng literal to avoid interning ssortingng s2 = new ssortingng(new char[] { 'H', 'a', 'l', 'l', 'o' }); bool equals = s1 == s2; // equals is false equals = ssortingng.Equals(s1, s2); // equals is true 

En plus de la réponse de Jon Skeet , j’aimerais expliquer pourquoi la plupart du temps, lorsque vous utilisez == vous obtenez la réponse true sur différentes instances de chaîne ayant la même valeur:

 ssortingng a = "Hell"; ssortingng b = "Hello"; a = a + "o"; Console.WriteLine(a == b); 

Comme vous pouvez le voir, a et b doivent être des instances de chaîne différentes, mais comme les chaînes sont immuables, le moteur d’exécution utilise ce qu’on appelle l’ internement de chaîne pour permettre à a et b référencer la même chaîne en mémoire. L’opérateur == pour les objects vérifie la référence, et comme a et b référencent la même instance, le résultat est true . Lorsque vous en modifiez une, une nouvelle instance de chaîne est créée, ce qui explique pourquoi l’internement de chaîne est possible.

Au fait, la réponse de Jon Skeet n’est pas complète. En effet, x == y est false mais c’est uniquement parce qu’il compare des objects et des objects comparés par référence. Si vous écrivez (ssortingng)x == (ssortingng)y , la réponse sera à nouveau true . Donc, les chaînes ont leur opérateur == – surchargé, qui appelle Ssortingng.Equals dessous.

Il y a beaucoup de réponses descriptives ici, donc je ne vais pas répéter ce qui a déjà été dit. Ce que je voudrais append est le code suivant qui montre toutes les permutations auxquelles je peux penser. Le code est assez long en raison du nombre de combinaisons. N’hésitez pas à le déposer dans MSTest et à voir la sortie pour vous-même (la sortie est incluse en bas).

Cette preuve soutient la réponse de Jon Skeet.

Code:

 [TestMethod] public void SsortingngEqualsMethodVsOperator() { ssortingng s1 = new SsortingngBuilder("ssortingng").ToSsortingng(); ssortingng s2 = new SsortingngBuilder("ssortingng").ToSsortingng(); Debug.WriteLine("ssortingng a = \"ssortingng\";"); Debug.WriteLine("ssortingng b = \"ssortingng\";"); TryAllSsortingngComparisons(s1, s2); s1 = null; s2 = null; Debug.WriteLine(ssortingng.Join(ssortingng.Empty, Enumerable.Repeat("-", 20))); Debug.WriteLine(ssortingng.Empty); Debug.WriteLine("ssortingng a = null;"); Debug.WriteLine("ssortingng b = null;"); TryAllSsortingngComparisons(s1, s2); } private void TryAllSsortingngComparisons(ssortingng s1, ssortingng s2) { Debug.WriteLine(ssortingng.Empty); Debug.WriteLine("-- ssortingng.Equals --"); Debug.WriteLine(ssortingng.Empty); Try((a, b) => ssortingng.Equals(a, b), s1, s2); Try((a, b) => ssortingng.Equals((object)a, b), s1, s2); Try((a, b) => ssortingng.Equals(a, (object)b), s1, s2); Try((a, b) => ssortingng.Equals((object)a, (object)b), s1, s2); Debug.WriteLine(ssortingng.Empty); Debug.WriteLine("-- object.Equals --"); Debug.WriteLine(ssortingng.Empty); Try((a, b) => object.Equals(a, b), s1, s2); Try((a, b) => object.Equals((object)a, b), s1, s2); Try((a, b) => object.Equals(a, (object)b), s1, s2); Try((a, b) => object.Equals((object)a, (object)b), s1, s2); Debug.WriteLine(ssortingng.Empty); Debug.WriteLine("-- a.Equals(b) --"); Debug.WriteLine(ssortingng.Empty); Try((a, b) => a.Equals(b), s1, s2); Try((a, b) => a.Equals((object)b), s1, s2); Try((a, b) => ((object)a).Equals(b), s1, s2); Try((a, b) => ((object)a).Equals((object)b), s1, s2); Debug.WriteLine(ssortingng.Empty); Debug.WriteLine("-- a == b --"); Debug.WriteLine(ssortingng.Empty); Try((a, b) => a == b, s1, s2); #pragma warning disable 252 Try((a, b) => (object)a == b, s1, s2); #pragma warning restore 252 #pragma warning disable 253 Try((a, b) => a == (object)b, s1, s2); #pragma warning restore 253 Try((a, b) => (object)a == (object)b, s1, s2); } public void Try(Expression> tryFunc, T1 in1, T2 in2) { T3 out1; Try(tryFunc, e => { }, in1, in2, out out1); } public bool Try(Expression> tryFunc, Action catchFunc, T1 in1, T2 in2, out T3 out1) { bool success = true; out1 = default(T3); try { out1 = tryFunc.Comstack()(in1, in2); Debug.WriteLine("{0}: {1}", tryFunc.Body.ToSsortingng(), out1); } catch (Exception ex) { Debug.WriteLine("{0}: {1} - {2}", tryFunc.Body.ToSsortingng(), ex.GetType().ToSsortingng(), ex.Message); success = false; catchFunc(ex); } return success; } 

Sortie:

 ssortingng a = "ssortingng"; ssortingng b = "ssortingng"; -- ssortingng.Equals -- Equals(a, b): True Equals(Convert(a), b): True Equals(a, Convert(b)): True Equals(Convert(a), Convert(b)): True -- object.Equals -- Equals(a, b): True Equals(Convert(a), b): True Equals(a, Convert(b)): True Equals(Convert(a), Convert(b)): True -- a.Equals(b) -- a.Equals(b): True a.Equals(Convert(b)): True Convert(a).Equals(b): True Convert(a).Equals(Convert(b)): True -- a == b -- (a == b): True (Convert(a) == b): False (a == Convert(b)): False (Convert(a) == Convert(b)): False -------------------- ssortingng a = null; ssortingng b = null; -- ssortingng.Equals -- Equals(a, b): True Equals(Convert(a), b): True Equals(a, Convert(b)): True Equals(Convert(a), Convert(b)): True -- object.Equals -- Equals(a, b): True Equals(Convert(a), b): True Equals(a, Convert(b)): True Equals(Convert(a), Convert(b)): True -- a.Equals(b) -- a.Equals(b): System.NullReferenceException - Object reference not set to an instance of an object. a.Equals(Convert(b)): System.NullReferenceException - Object reference not set to an instance of an object. Convert(a).Equals(b): System.NullReferenceException - Object reference not set to an instance of an object. Convert(a).Equals(Convert(b)): System.NullReferenceException - Object reference not set to an instance of an object. -- a == b -- (a == b): True (Convert(a) == b): True (a == Convert(b)): True (Convert(a) == Convert(b)): True 

Il est clair que tvi.header n’est pas une Ssortingng . Le == est un opérateur surchargé par la classe Ssortingng , ce qui signifie qu’il ne fonctionnera que si le compilateur sait que les deux côtés de l’opérateur sont Ssortingng .

Un object est défini par un OBJECT_ID, qui est unique. Si A et B sont des objects et que A == B est vrai, alors ils sont exactement le même object, ils ont les mêmes données et méthodes, mais cela est également vrai:

A.OBJECT_ID == B.OBJECT_ID

si A.Equals (B) est vrai, cela signifie que les deux objects sont dans le même état, mais cela ne signifie pas que A est identique à B.

Les chaînes sont des objects.

Notez que les opérateurs == et Equals sont réflexifs, simésortingques, tranzitifs, ils sont donc des relations équivalentes (pour utiliser des termes algébriques relationnels)

Ce que cela signifie: Si A, B et C sont des objects, alors:

(1) A == A est toujours vrai; A.Equals (A) est toujours vrai (réflexivité)

(2) si A == B alors B == A; Si A.Equals (B) alors B.Equals (A) (simetry)

(3) si A == B et B == C, alors A == C; si A.Equals (B) et B.Equals (C) puis A.Equals (C) (tranzitivity)

En outre, vous pouvez noter que cela est également vrai:

(A == B) => (A.Equals (B)), mais l’inverse n’est pas vrai.

 AB => 0 0 1 0 1 1 1 0 0 1 1 1 

Exemple de vie réelle: Deux Hamburgers du même type ont les mêmes propriétés: ce sont des objects de la classe Hamburger, leurs propriétés sont exactement les mêmes, mais ce sont des entités différentes. Si vous achetez ces deux Hamburgers et en mangez un, l’autre ne sera pas mangé. Donc, la différence entre Equals et ==: Vous avez hamburger1 et hamburger2. Ils sont exactement dans le même état (même poids, même température, même goût), donc hamburger1.Equals (hamburger2) est vrai. Mais hamburger1 == hamburger2 est faux, car si l’état de hamburger1 change, l’état de hamburger2 ne change pas nécessairement et vice versa.

Si vous et un ami recevez un Hamburger, qui est le vôtre et le sien en même temps, alors vous devez décider de diviser le Hamburger en deux parties, car you.getHamburger () == friend.getHamburger () est vrai et si cela se produit : friend.eatHamburger (), votre Hamburger sera également mangé.

Je pourrais écrire d’autres nuances sur Equals et ==, mais je commence à avoir faim, alors je dois y aller.

Cordialement, Lajos Arpad.