Perché C # e Java usano l'uguaglianza di riferimento come predefinita per '=='?

30

Ho riflettuto per un po 'perché Java e C # (e sono sicuro che altre lingue) abbiano come predefinito riferimento all'uguaglianza per == .

Nella programmazione che faccio (che è certamente solo un piccolo sottoinsieme di problemi di programmazione), quasi sempre voglio l'uguaglianza logica quando si confrontano gli oggetti anziché l'uguaglianza di riferimento. Stavo cercando di capire perché entrambi questi linguaggi hanno seguito questa rotta invece di invertirla e avere == essere uguaglianza logica e usare .ReferenceEquals() per l'uguaglianza di riferimento.

Ovviamente l'utilizzo dell'eguaglianza di riferimento è molto semplice da implementare e fornisce un comportamento molto coerente, ma non sembra che si adatti bene alla maggior parte delle pratiche di programmazione che vedo oggi.

Non voglio sembrare ignorante dei problemi con il tentativo di implementare un confronto logico e che deve essere implementato in ogni classe. Mi rendo anche conto che queste lingue sono state progettate molto tempo fa, ma la domanda generale sta in piedi.

Ci sono alcuni importanti vantaggi del default a ciò che mi manca o mi sembra ragionevole che il comportamento predefinito dovrebbe essere l'uguaglianza logica, e il default torna all'uguaglianza di riferimento che un'uguaglianza logica non esiste per la classe?

    
posta Zipper 09.06.2013 - 06:24
fonte

5 risposte

28

C # lo fa perché Java lo ha fatto. Java ha fatto perché Java non supporta l'overloading dell'operatore. Poiché l'uguaglianza dei valori deve essere ridefinita per ogni classe, non può essere un operatore, ma deve essere invece un metodo. IMO questa è stata una decisione sbagliata. È molto più facile scrivere e leggere a == b di a.equals(b) , e molto più naturale per i programmatori con esperienza C o C ++, ma a == b è quasi sempre sbagliato. I bug dall'uso di == in cui era richiesto .equals hanno sprecato migliaia di ore di programmazione.

    
risposta data 09.06.2013 - 07:13
fonte
14

La risposta breve: Consistenza

Per rispondere correttamente alla tua domanda, tuttavia, ti suggerisco di fare un passo indietro e di esaminare il problema di cosa significa uguaglianza in un linguaggio di programmazione. Ci sono almeno TRE diverse possibilità, che sono usate in varie lingue:

  • Equality di riferimento : significa che a = b è vero se a e b si riferiscono allo stesso oggetto. Non sarebbe vero se a e b si riferivano a oggetti diversi, anche se tutti gli attributi di aeb erano uguali.
  • Equality Shallow : significa che a = b è vero se tutti gli attributi degli oggetti a cui a e b si riferiscono sono identici. L'estrema uguaglianza può essere facilmente implementata mediante un confronto bit a bit dello spazio di memoria che rappresenta i due oggetti. Si prega di notare che l'uguaglianza di riferimento implica l'uguaglianza superficiale
  • Uguaglianza profonda : significa che a = b è vero se ogni attributo in a e b è identico o profondamente uguale. Si noti che l'uguaglianza profonda è implicita sia dall'uguaglianza di riferimento sia dall'uguaglianza superficiale. In questo senso, l'uguaglianza profonda è la forma più debole di uguaglianza e l'uguaglianza di riferimento è la più strong.

Questi tre tipi di uguaglianza sono spesso usati perché sono convenienti da implementare: tutti e tre i controlli di uguaglianza possono essere generati facilmente da un compilatore (nel caso dell'eguaglianza profonda, il compilatore potrebbe aver bisogno di usare i bit di tag per prevenire cicli infiniti se una struttura da confrontare ha riferimenti circolari). Ma c'è un altro problema: nessuno di questi potrebbe essere appropriato.

Nei sistemi non banali, l'uguaglianza degli oggetti è spesso definita come qualcosa tra l'uguaglianza profonda e di riferimento. Per verificare se vogliamo considerare due oggetti uguali in un certo contesto, potremmo richiedere che alcuni attributi siano confrontati da dove si trova nella memoria e altri da un'eguaglianza profonda, mentre alcuni attributi possono essere completamente diversi. Quello che ci piacerebbe davvero è un "quarto tipo di uguaglianza", davvero bello, spesso chiamato in letteratura uguaglianza semantica . Le cose sono uguali se sono uguali, nel nostro dominio. =)

Quindi possiamo tornare alla tua domanda:

Is there some major benefit of defaulting to this that I am simply missing, or does it seem reasonable that the default behavior should be logical equality, and defaulting back to reference equality if a logical equality doesn't exist for the class?

Che cosa intendiamo quando scriviamo 'a == b' in qualsiasi lingua? Idealmente, dovrebbe sempre essere lo stesso: l'uguaglianza semantica. Ma non è possibile.

Una delle considerazioni principali è che, almeno per tipi semplici come i numeri, ci aspettiamo che due variabili siano uguali dopo l'assegnazione dello stesso valore. Vedi sotto:

var a = 1;
var b = a;
if (a == b){
    ...
}
a = 3;
b = 3;
if (a == b) {
    ...
}

In questo caso, ci aspettiamo che 'a uguale a b' in entrambe le dichiarazioni. Qualsiasi altra cosa sarebbe pazzesca. La maggior parte (se non tutte) delle lingue seguono questa convenzione. Pertanto, con tipi semplici (cioè valori) sappiamo come raggiungere l'uguaglianza semantica. Con gli oggetti, questo può essere qualcosa di completamente diverso. Vedi sotto:

var a = new Something(1);
var b = a;
if (a == b){
    ...
}
b = new Something(1);
a.DoSomething();
b.DoSomething();
if (a == b) {
    ...
}

Ci aspettiamo che il primo "se" sia sempre vero. Ma cosa ti aspetti al secondo "se"? Dipende davvero. Can DoSomething può cambiare l'uguaglianza (semantica) di aeb?

Il problema dell'eguaglianza semantica è che non può essere generato automaticamente dal compilatore per gli oggetti, né è ovvio dai compiti . È necessario fornire un meccanismo per l'utente per definire l'uguaglianza semantica. Nei linguaggi orientati agli oggetti, quel meccanismo è un metodo ereditato: uguale a . Leggendo un pezzo di codice OO, non ci si aspetta che un metodo abbia la stessa esatta implementazione in tutte le classi. Siamo abituati all'ereditarietà e al sovraccarico.

Con gli operatori, tuttavia, ci aspettiamo lo stesso comportamento. Quando vedi 'a == b' dovresti aspettarti lo stesso tipo di uguaglianza (dal 4 sopra) in tutte le situazioni. Pertanto, mirando alla coerenza i designer di lingua hanno utilizzato l'uguaglianza di riferimento per tutti i tipi. Non dovrebbe dipendere dal fatto che un programmatore abbia sovrascritto un metodo o meno.

PS: il linguaggio Dee è leggermente diverso da Java e C #: l'operatore di uguaglianza significa uguaglianza poco profonda per i tipi semplici e l'uguaglianza semantica per le classi definite dall'utente (con la responsabilità di implementare l'operazione = che giace con l'utente - nessun valore predefinito è fornito). Come per i tipi semplici, l'uguaglianza superficiale è sempre l'uguaglianza semantica, il linguaggio è coerente. Il prezzo che paga, tuttavia, è che l'operatore di uguale è indefinito per i tipi definiti dall'utente. Devi implementarlo. E, a volte, è semplicemente noioso.

    
risposta data 12.06.2013 - 21:42
fonte
0

I was trying to think of why both of these languages went this route instead of inverting it and having == be logical equality and using .ReferenceEquals() for reference equality.

Perché quest'ultimo approccio sarebbe fonte di confusione. Prendere in considerazione:

if (null.ReferenceEquals(null)) System.out.println("ok");

Questo codice dovrebbe stampare "ok" , o dovrebbe lanciare un NullPointerException ?

    
risposta data 08.05.2015 - 08:27
fonte
-2

Per Java e C # il vantaggio sta nel loro essere orientato agli oggetti.

Da un punto di vista delle prestazioni - il codice più facile da scrivere dovrebbe essere anche più veloce: poiché OOP intende che elementi logicamente distinti siano rappresentati da oggetti diversi, controllare che l'uguaglianza di riferimento sia più veloce, tenendo conto considera che gli oggetti possono diventare abbastanza grandi.

Da un punto di vista logico - l'uguaglianza di un oggetto a un altro non deve essere ovvio quanto il confronto con le proprietà dell'oggetto per l'uguaglianza (ad esempio, come null == null interpretato logicamente? può differire da caso a caso).

Penso a cosa si riduce, è la tua osservazione che "vuoi sempre uguaglianza logica rispetto all'uguaglianza di riferimento". Il consenso tra i progettisti linguistici era probabilmente il contrario. Personalmente trovo difficile valutarlo, poiché mi manca l'ampio spettro dell'esperienza di programmazione. Approssimativamente, utilizzo l'uguaglianza di riferimento più negli algoritmi di ottimizzazione e l'uguaglianza logica più nella gestione dei set di dati.

    
risposta data 09.06.2013 - 07:32
fonte
-3

.equals() confronta le variabili in base al loro contenuto. invece di == che confronta gli oggetti in base al loro contenuto ...

usare gli oggetti è più preciso tu usi .equals()

    
risposta data 12.06.2013 - 22:55
fonte

Leggi altre domande sui tag