Perché C # non riesce a confrontare due tipi di oggetti tra loro ma VB no?


152

Ho due oggetti in C # e non so se è booleano o qualsiasi altro tipo. Tuttavia, quando provo a confrontare quei C # non riesce a dare la risposta giusta. Ho provato lo stesso codice con VB.NET e lo ha fatto!

Qualcuno può dirmi come risolvere questo problema se esiste una soluzione?

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

3
cosa succede se si modifica il comparatore di uguaglianza in a.Equals(b)?
Jason Meckley,

8
Questa è una buona domanda per scopi pedagogici.
Lobo,

10
Perché il tuo codice VB.NET non è uguale al tuo codice C #.
Segugio di sicurezza,

9
Quando ti assegni aottieni boxe e crea una scatola contenente true. Quando ti assegni bottieni un'altra casella contenente anche true. Quando si confronta ae b, poiché entrambi sono di tipo tempo di compilazione object, si chiama il sovraccarico operator ==(object, object)definito dalla specifica del linguaggio C #. Questo sovraccarico controlla se i riferimenti vanno allo stesso oggetto. Poiché hai due caselle, il risultato è falsee la frase "sotto" ifnon verrà eseguita. Per capirlo meglio, prova a cambiare il compito bin questo: object b = a;ora hai solo una casella.
Jeppe Stig Nielsen,

3
Ho avuto occasione prima di dire "Fai attenzione supponendo che VB.NET e C # siano la stessa lingua parlata con un accento diverso - non lo sono"
AakashM,

Risposte:


168

In C #, l' ==operatore (quando applicato alle espressioni del tipo di riferimento) esegue un controllo dell'uguaglianza di riferimento a meno che non sia sovraccarico . Stai confrontando due riferimenti che sono il risultato di conversioni di boxe, quindi quelli sono riferimenti distinti.

EDIT: con tipi che sovraccaricano il ==, è possibile ottenere un comportamento diverso, ma che si basa sul tipo di espressioni in fase di compilazione . Ad esempio, stringfornisce ==(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

Qui il primo confronto utilizza l'operatore sovraccarico, ma il secondo utilizza il confronto di riferimento "predefinito".

In VB, l' =operatore fa molto più lavoro - non è nemmeno equivalente all'utilizzo object.Equals(x, y), in quanto cose come Option Comparepossono influenzare il modo in cui il testo viene confrontato.

Fondamentalmente gli operatori non lavorano allo stesso modo e non sono destinati a lavorare allo stesso modo.


17
+1 Sapevo che saresti stato in giro, ADORO questo tipo di domande misteriose :)
Abdusalam Ben Haj,

3
@AbZy: Speravo di essere in grado di fornire una spiegazione più dettagliata di ciò che ha =fatto in VB, ma le specifiche non sono tremendamente chiare.
Jon Skeet,

cosa interessante, ma cambiare l'oggetto in dinamico si comporta come VB
VladL,

4
@VladL: Sì, perché poi passerà per i tipi di tempo di esecuzione ed eseguirà il bool == boolconfronto.
Jon Skeet,

1
@Mahdi Lobo potrebbe aver fornito il codice, ma la sua risposta è sbagliata, a differenza di quella di Jon.
Servito il

79

Oltre alla risposta di Jon che spiega il lato C # delle cose, ecco cosa fa VB:

In VB con Option Strict On, un confronto tramite verifica = sempre l' uguaglianza di valore e mai l'uguaglianza di riferimento. In effetti, il tuo codice non si compila nemmeno una volta che cambi Option Strict Onperché System.Objectnon definisce un Operator=. Dovresti sempre avere questa opzione attiva, cattura i bug in modo più efficace rispetto a un acchiappamosche di Venere (anche se nel tuo caso particolare questo comportamento rilassato fa davvero la cosa giusta). 1

In effetti, con Option Strict OnVB si comporta anche più rigorosamente di C #: in C #, a == b o avvia una chiamata SomeType.operator==(a, b)o, se non esiste, invoca il confronto di uguaglianza di riferimento (che equivale a chiamare object.ReferenceEquals(a, b)).

In VB, invece, il confronto invoca a = b sempre l'operatore di uguaglianza. 2 Se si desidera utilizzare il confronto di uguaglianza di riferimento, è necessario utilizzare a Is b(che è, ancora una volta, lo stesso di Object.ReferenceEquals(a, b)).


1) Ecco una buona indicazione del perché l'utilizzo Option Strict Offsia una cattiva idea: ho usato VB.NET per quasi un decennio, da prima della versione ufficiale di .NET fino a qualche anno fa, e non ho assolutamente idea di cosa a = bfaccia Option Strict Off. Fa una sorta di confronto sull'uguaglianza, ma cosa succede esattamente e perché, nessuna idea. dynamicTuttavia, è più complesso della funzionalità di C # (perché si basa su un'API ben documentata). Ecco cosa dice MSDN:

Poiché Option Strict Onfornisce una digitazione forte , impedisce conversioni di tipo non intenzionali con perdita di dati, non consente l'associazione tardiva e migliora le prestazioni, è fortemente consigliato l'utilizzo.

2) Jon ha menzionato un'eccezione, le stringhe, in cui il confronto di uguaglianza fa alcune cose in più per ragioni di retrocompatibilità.


4
+1. Penso che questo sia un caso in cui i progettisti di VB.NET sono riusciti a far "funzionare" il linguaggio per i programmatori provenienti da VB6 e VBA, in cui OOP è molto meno importante e quindi il concetto di uguaglianza di riferimento è molto meno importante. Un programmatore VB può scrivere un buon codice di lavoro senza pensare molto agli oggetti e così via.
John M Gant,

5
+1 Questo non è votato come dovrebbe realmente. Non usare Option Strict Ondeve essere considerato un reato ...
Deer Hunter

1
@JohnMGant: un programmatore che non comprende il significato dell'identità di riferimento potrebbe essere in grado di scrivere codice che funziona, ma è improbabile che sappia davvero quali cose possono essere cambiate in modo sicuro, quali cambiamenti possono sempre rompere le cose e quali cambiamenti possono sembrano funzionare ma causano cattivi effetti collaterali indesiderati (ad esempio causando quelli che dovrebbero essere riferimenti a diversi oggetti mutabili che hanno lo stesso stato invece di essere riferimenti allo stesso oggetto). Se gli oggetti vengono raramente mutati, una modifica di questo tipo potrebbe non causare problemi immediati, ma potrebbe causare la comparsa di bug difficili da trovare in seguito.
supercat

4

Le istanze dell'oggetto non vengono confrontate con l'operatore "==". Dovresti usare il metodo "uguale a". Con "==" l'operatore confronta i riferimenti, non gli oggetti.

Prova questo:

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");
}

risultati:

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

Ora prova questo:

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");
}

risultati:

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

1
Questo semplicemente perché non hai ignorato operator ==. Se si annulla tale operatore e non è uguale, l'output verrà invertito. Non c'è nulla di inerente al confronto di riferimento operator ==e nulla di inerente al confronto di valori in Equals. Sono solo due modi per determinare l'uguaglianza; entrambi hanno implementazioni predefinite di un confronto di riferimento ed entrambi possono essere ignorati per fare qualunque cosa tu voglia che facciano. L'unica altra differenza è che Equalsè virtuale e operator ==non lo è.
Servito il

1
@Servy: nota che non puoi ignorare == , puoi solo sovraccaricarlo .
Jon Skeet,

1
Spiacente, -1. Questa risposta è semplicemente errata e non dovrebbe essere quella accettata.
Konrad Rudolph,

Da qualche parte c'è una domanda Java in attesa di questa risposta.
Chad Schouggins,

3

Il problema è che l'operatore == in C # è una chiamata a un metodo statico (beh, forse non tecnicamente, ma può essere considerato come tale) in base al tipo di tempo di compilazione dei due parametri. Non importa quali siano i tipi di runtime effettivi di tali oggetti.

In base a quel tipo di tempo di compilazione, il compilatore determinerà quale implementazione operator ==utilizzare. Potrebbe utilizzare l' objectimplementazione predefinita , potrebbe utilizzare uno dei sovraccarichi numerici forniti dalla lingua o potrebbe essere un'implementazione definita dall'utente.

Ciò è diverso da VB in quanto VB non determina l'implementazione in fase di compilazione. Attende fino al runtime e ispeziona i due parametri forniti per determinare quale implementazione ==dell'operatore deve utilizzare.

Il codice contiene valori booleani, ma si trovano in variabili di tipo object. Poiché la variabile è di tipo object, il compilatore C # utilizza l' objectimplementazione di ==, che confronta i riferimenti , non le istanze dell'oggetto. Poiché i valori booleani sono caselle, non hanno lo stesso riferimento, anche se i loro valori sono gli stessi.

Il codice VB non si preoccupa del tipo di variabile. Aspetta fino al runtime e quindi controlla le due variabili, vede che sono effettivamente entrambi di tipo booleano e quindi utilizza l' ==implementazione dell'operatore booleano . Tale implementazione confronta i valori dei booleani, non i loro riferimenti (e i booleani verranno decompressi prima di chiamare l'operatore, quindi un confronto di riferimento non ha più senso). Poiché i valori dei booleani sono gli stessi, restituisce true.


Sembra perfetto per il C #; Non so abbastanza su cosa esattamente =in VB dire per certo.
Jon Skeet,

@JonSkeet Abbastanza giusto.
Servito il

Per msdn.microsoft.com/en-us/library/cey92b0t(v=vs.110).aspx , nella sezione "Programmazione Senza tipo con relazionali Operatori di confronto": =, insieme a tutti gli altri operatori di confronto relazionali come <, >=, etc. , ricevono un trattamento speciale quando entrambi o entrambi i lati dell'operatore lo sono Object. Questo trattamento speciale viene fatto in modo tale che i programmatori VB6, che sono abituati a utilizzare un tipo noto come Variantpre.NET VB, possano fare uso di ObjectVB.Net nei modi che hanno usato in Variantprecedenza.
rskar,

Per dirla in un altro modo, e lasciando da parte gli effetti del sovraccarico e Option Strict On, VB =è distorto verso unboxing e Objectfino a quando non può arrivare a una stringa o un numero.
rskar,
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.