Pourquoi C # ne parvient pas à comparer deux types d'objets entre eux, mais pas VB?

152

J'ai deux objets en C # et je ne sais pas si c'est booléen ou tout autre type. Cependant, lorsque j'essaye de comparer ces C #, je ne donne pas la bonne réponse. J'ai essayé le même code avec VB.NET et ça l'a fait!

Quelqu'un peut-il me dire comment résoudre ce problème s'il existe une solution?

C #:

object a = true;
object b = true;
object c = false;
if (a == b) c = true;
MessageBox.Show(c.ToString()); //Outputs False !!

VB.NET:

Dim a As Object = True
Dim b As Object = True
Dim c As Object = False
If (a = b) Then c = True
MessageBox.Show(c.ToString()) '// Outputs True
Mohsen Sarkar
la source
3
et si vous changez le comparateur d'égalité en a.Equals(b)?
Jason Meckley
8
C'est une bonne question à des fins pédagogiques.
Lobo
10
Parce que votre code VB.NET n'est pas égal à votre code C #.
Security Hound
9
Lorsque vous vous assignez avous obtenez la boxe et créez une boîte contenant true. Lorsque vous attribuez à bvous obtenez une autre boîte contenant également true. Lorsque vous comparez aet b, comme les deux sont de type à la compilation object, vous appelez la surcharge operator ==(object, object)définie par la spécification du langage C #. Cette surcharge vérifie si les références vont au même objet. Puisque vous avez deux boîtes, le résultat est false, et l'instruction "sous" votre ifne fonctionnera pas. Pour mieux comprendre cela, essayez de modifier l'attribution de bà ceci: object b = a;Vous n'avez plus qu'une seule case.
Jeppe Stig Nielsen
3
J'ai déjà eu l'occasion de dire "Soyez prudent en supposant que VB.NET et C # sont la même langue parlée avec un accent différent - ils ne le sont pas"
AakashM

Réponses:

168

En C #, l' ==opérateur (lorsqu'il est appliqué aux expressions de type référence) effectue une vérification d'égalité de référence à moins qu'il ne soit surchargé . Vous comparez deux références qui sont le résultat de conversions de boxe, donc ce sont des références distinctes.

EDIT: Avec les types qui surchargent le ==, vous pouvez obtenir un comportement différent - mais cela est basé sur le type au moment de la compilation des expressions. Par exemple, stringfournit ==(string, string):

string x = new string("foo".ToCharArray());
string y = new string("foo".ToCharArray());
Console.WriteLine(x == y); // True
Console.WriteLine((object) x == (object) y); // False

Ici, la première comparaison utilise l'opérateur surchargé, mais la seconde utilise la comparaison de référence "par défaut".

En VB, l' =opérateur fait beaucoup plus de travail - ce n'est même pas simplement équivalent à utiliser object.Equals(x, y), comme des choses commeOption Compare peuvent affecter la façon dont le texte est comparé.

Fondamentalement, les opérateurs ne fonctionnent pas de la même manière et ne sont pas destinés à fonctionner de la même manière.

Jon Skeet
la source
17
+1 Je savais que vous seriez dans les parages, vous ADOREZ ce genre de questions mystérieuses :)
Abdusalam Ben Haj
3
@AbZy: J'espérais être en mesure de fournir une explication plus détaillée de ce =qui se passait dans VB, mais les spécifications ne sont pas très claires.
Jon Skeet
chose intéressante, mais le changement d'objet en dynamique se comporte de la même manière que VB
VladL
4
@VladL: Oui, parce qu'alors il passera par les types d'exécution et effectuera la bool == boolcomparaison.
Jon Skeet
1
@Mahdi Lobo a peut-être fourni du code, mais sa réponse est également fausse, contrairement à celle de Jon.
Servy le
79

En plus de la réponse de Jon qui explique le côté C # des choses, voici ce que fait VB:

Dans VB with Option Strict On, une comparaison via teste = toujours l' égalité des valeurs et jamais l'égalité des références. En fait, votre code ne se compile même pas une fois que vous basculez Option Strict Oncar System.Objectil ne définit pas de fichier Operator=. Vous devriez toujours avoir cette option activée, elle détecte les bogues plus efficacement qu'un piège à mouches venus (bien que dans votre cas particulier, ce comportement laxiste fasse réellement la bonne chose). 1

En fait, avec Option Strict On, VB se comporte encore plus stricte que C #: en C #, a == b soit déclenche un appel à, SomeType.operator==(a, b)soit, si cela n'existe pas, appelle la comparaison d'égalité de référence (ce qui équivaut à appeler object.ReferenceEquals(a, b)).

En VB par contre, la comparaison invoque a = b toujours l'opérateur d'égalité. 2 Si vous souhaitez utiliser la comparaison d'égalité de référence, vous devez utiliser a Is b(qui est, encore une fois, le même que Object.ReferenceEquals(a, b)).


1) Voici une bonne indication des raisons pour lesquelles utiliser Option Strict Offest une mauvaise idée: j'utilise VB.NET depuis près d'une décennie, depuis la sortie officielle de .NET jusqu'à il y a quelques années, et je n'ai absolument aucune idée de ce a = bque cela signifie Option Strict Off. Il fait une sorte de comparaison d'égalité, mais ce qui se passe exactement et pourquoi, aucune idée. C'est plus complexe que la fonctionnalité de C # dynamic, cependant (car cela repose sur une API bien documentée). Voici ce que dit le MSDN:

Parce que Option Strict Onfournit un typage fort , empêche les conversions de type involontaires avec perte de données, interdit la liaison tardive et améliore les performances, son utilisation est fortement recommandée.

2) Jon a mentionné une exception, les chaînes, où la comparaison d'égalité fait encore plus de choses pour des raisons de compatibilité ascendante.

Konrad Rudolph
la source
4
+1. Je pense que c'est un cas où les concepteurs de VB.NET ont réussi à faire «juste fonctionner» le langage pour les programmeurs venant de VB6 et VBA, où la POO est beaucoup moins proéminente et donc le concept d'égalité de référence est beaucoup moins important. Un codeur VB peut écrire du bon code de travail sans trop penser aux objets et ainsi de suite.
John M Gant
5
+1 Ce n'est pas autant de vote positif qu'il le devrait. Ne pas utiliser Option Strict Ondoit être considéré comme une infraction pénale ...
Deer Hunter
1
@JohnMGant: Un codeur qui ne comprend pas la signification de l'identité de référence peut être capable d'écrire du code qui fonctionne, mais il est peu probable qu'il sache vraiment ce qui peut être changé en toute sécurité, quels changements casseront toujours les choses et quels changements peuvent semblent fonctionner mais provoquent des effets secondaires indésirables indésirables (par exemple, faire en sorte que ce qui devrait être des références à différents objets mutables ayant le même état soit plutôt des références au même objet). Si les objets sont rarement mutés, un tel changement peut ne pas causer de problèmes immédiats, mais peut faire surgir des bogues difficiles à trouver plus tard.
supercat du
4

Les instances d'objet ne sont pas comparées à l'opérateur "==". Vous devez utiliser la méthode «égal». Avec l'opérateur "==", comparez des références, pas des objets.

Essaye ça:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }
}

MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

Résultats:

a reference is not equal to b reference
a object is not equal to b object

Maintenant, essayez ceci:

public class MyObject
{
    public MyObject(String v)
    {
        Value = v;
    }
    public String Value { get; set; }

    public bool Equals(MyObject o)
    {
        return (Value.CompareTo(o.Value)==0);
    }
}
MyObject a = new MyObject("a");
MyObject b = new MyObject("a");
if(a==b){
    Debug.WriteLine("a reference is equal to b reference");
}else{
    Debug.WriteLine("a reference is not equal to b reference");
}
if (a.Equals(b)) {
    Debug.WriteLine("a object is equal to b object");
} else {
    Debug.WriteLine("a object is not equal to b object");
}

Résultats:

a reference is not equal to b reference
a object is equal to b object
Lobo
la source
1
C'est simplement parce que vous n'avez pas remplacé operator ==. Si vous remplacez cet opérateur et qu'il n'est pas égal, votre sortie serait inversée. Il n'y a rien d'inhérent à la comparaison de références operator ==et rien d'inhérent à la comparaison de valeurs dans Equals. Ce ne sont que deux façons de déterminer l'égalité; les deux ont des implémentations par défaut d'une comparaison de référence, et les deux peuvent être remplacés pour faire ce que vous voulez qu'ils fassent. La seule autre différence est que Equalsc'est virtuel et operator ==ne l'est pas.
Servy le
1
@Servy: Notez que vous ne pouvez pas remplacer == - vous ne pouvez que le surcharger .
Jon Skeet
1
Désolé, -1. Cette réponse est tout simplement incorrecte et ne devrait pas être acceptée.
Konrad Rudolph
Quelque part, une question Java attend cette réponse.
Chad Schouggins
3

Le problème est que l'opérateur == en C # est un appel à une méthode statique (enfin, peut-être pas techniquement, mais cela peut être considéré comme tel) basé sur le type de compilation des deux paramètres. Les types d'exécution réels de ces objets n'ont pas d'importance.

En fonction de ce type de compilation, le compilateur déterminera quelle implémentation operator ==utiliser. Il peut utiliser l' objectimplémentation par défaut , il peut utiliser l'une des surcharges numériques fournies par le langage, ou il peut s'agir d'une implémentation définie par l'utilisateur.

Ceci est différent de VB en ce que VB ne détermine pas l'implémentation au moment de la compilation. Il attend l'exécution et inspecte les deux paramètres qui lui sont donnés pour déterminer quelle implémentation de l' ==opérateur il doit utiliser.

Votre code contient des valeurs booléennes, mais elles sont dans des variables de type object. Étant donné que la variable est de type object, le compilateur C # utilise l' objectimplémentation de ==, qui compare les références , et non les instances d'objet. Puisque les valeurs booléennes sont des boîtes, elles n'ont pas la même référence, même si leurs valeurs sont les mêmes.

Le code VB ne se soucie pas du type de la variable. Il attend l'exécution, puis vérifie les deux variables, voit qu'elles sont en fait de type booléen et utilise donc l' ==implémentation de l'opérateur booléen . Cette implémentation compare les valeurs des booléens, pas leurs références (et les booléens seront déballés avant d'appeler cet opérateur, donc une comparaison de références n'a même plus de sens). Étant donné que les valeurs des booléens sont les mêmes, il renvoie true.

Servy
la source
Cela semble bien pour le C #; Je ne sais pas assez exactement ce que =fait VB pour être sûr.
Jon Skeet
@JonSkeet Très bien.
Servy le
Par msdn.microsoft.com/en-us/library/cey92b0t(v=vs.110).aspx , dans la section « Programmation avec comparaison sans type Relational Opérateurs »: =, ainsi que tous les autres opérateurs de comparaison relationnels tels que <, >=, etc. , reçoivent un traitement spécial lorsque les deux côtés de l'opérateur ou l'un ou l'autre le sont Object. Ce traitement spécial est fait pour que les programmeurs VB6, qui sont habitués à utiliser un type connu sous le nom Variantde pre-.NET VB, puissent utiliser Objectdans VB.Net de la manière qu'ils ont utilisée Variantauparavant.
rskar
Pour le dire autrement, et en laissant de côté les effets de la surcharge et Option Strict On, VB =est biaisé vers le déballage et Objectjusqu'à ce qu'il puisse atteindre une chaîne ou un numérique.
rskar