Non posso parlare per i progettisti del linguaggio, ma da quello su cui posso ragionare, sembra che sia stata una decisione intenzionale e corretta sul design.
Guardando questo codice F # di base, puoi compilarlo in una libreria funzionante. Questo è il codice legale per F # e sovraccarica solo l'operatore di uguaglianza, non la disuguaglianza:
module Module1
type Foo() =
let mutable myInternalValue = 0
member this.Prop
with get () = myInternalValue
and set (value) = myInternalValue <- value
static member op_Equality (left : Foo, right : Foo) = left.Prop = right.Prop
//static member op_Inequality (left : Foo, right : Foo) = left.Prop <> right.Prop
Questo fa esattamente quello che sembra. Crea solo un comparatore di uguaglianza ==
e verifica se i valori interni della classe sono uguali.
Sebbene non sia possibile creare una classe come questa in C #, è possibile utilizzarne una compilata per .NET. È ovvio che utilizzerà il nostro operatore sovraccarico per ==
So, a cosa serve il runtime !=
?
Lo standard C # EMCA ha un sacco di regole (sezione 14.9) che spiegano come determinare quale operatore utilizzare per valutare l'uguaglianza. Per dirlo eccessivamente semplificato e quindi non perfettamente accurato, se i tipi che vengono confrontati sono dello stesso tipo e c'è un operatore di uguaglianza sovraccarico presente, userà quel sovraccarico e non l'operatore di uguaglianza di riferimento standard ereditato da Object. Non sorprende quindi che, se è presente solo uno degli operatori, utilizzerà l'operatore di uguaglianza di riferimento predefinito, che tutti gli oggetti hanno, non vi è un sovraccarico per esso. 1
Sapendo che è così, la vera domanda è: perché è stato progettato in questo modo e perché il compilatore non lo capisce da solo? Molte persone dicono che questa non è stata una decisione di progettazione, ma mi piace pensare che sia stata pensata in questo modo, soprattutto per quanto riguarda il fatto che tutti gli oggetti hanno un operatore di uguaglianza predefinito.
Quindi, perché il compilatore non crea automagicamente l' !=
operatore? Non posso esserne sicuro, a meno che qualcuno di Microsoft non lo confermi, ma questo è ciò che posso determinare basandomi sui fatti.
Per prevenire comportamenti imprevisti
Forse voglio fare un confronto di valore ==
per testare l'uguaglianza. Tuttavia, quando si trattava di !=
me non mi importava affatto se i valori fossero uguali a meno che il riferimento fosse uguale, perché per il mio programma di considerarli uguali, mi importa solo se i riferimenti corrispondono. Dopotutto, questo è effettivamente definito come comportamento predefinito del C # (se entrambi gli operatori non fossero sovraccarichi, come nel caso di alcune librerie .net scritte in un'altra lingua). Se il compilatore aggiungesse automaticamente il codice, non potevo più fare affidamento sul compilatore per ottenere il codice di output che dovrebbe essere conforme. Il compilatore non dovrebbe scrivere codice nascosto che modifica il comportamento del tuo, soprattutto quando il codice che hai scritto rientra negli standard sia di C # che della CLI.
In termini di ciò che ti costringe a sovraccaricarlo, invece di passare al comportamento predefinito, posso solo affermare con fermezza che è nello standard (EMCA-334 17.9.2) 2 . Lo standard non specifica il perché. Credo che ciò sia dovuto al fatto che C # prende in prestito molti comportamenti dal C ++. Vedi sotto per ulteriori informazioni al riguardo.
Quando si esegue l' override !=
e ==
non è necessario restituire bool.
Questa è un'altra probabile ragione. In C #, questa funzione:
public static int operator ==(MyClass a, MyClass b) { return 0; }
è valido come questo:
public static bool operator ==(MyClass a, MyClass b) { return true; }
Se stai restituendo qualcosa di diverso da bool, il compilatore non può inferire automaticamente un tipo opposto. Inoltre, nel caso in cui l'operatore fa bool ritorno, semplicemente non ha senso per loro creano generare il codice che esisterebbe solo in quel caso uno specifico o, come ho detto sopra, il codice che nasconde il comportamento predefinito del CLR.
C # prende in prestito molto da C ++ 3
Quando è stato introdotto C #, c'era un articolo nella rivista MSDN che scriveva, parlando di C #:
Molti sviluppatori desiderano che esistesse un linguaggio facile da scrivere, leggere e mantenere come Visual Basic, ma che fornisse comunque la potenza e la flessibilità del C ++.
Sì, l'obiettivo di progettazione per C # era quello di fornire quasi la stessa quantità di energia del C ++, sacrificando solo un po 'per comodità come la rigida sicurezza dei tipi e la raccolta dei rifiuti. C # è stato fortemente modellato su C ++.
Potresti non essere sorpreso di apprendere che in C ++ gli operatori di uguaglianza non devono restituire bool , come mostrato in questo programma di esempio
Ora, C ++ non richiede direttamente di sovraccaricare l'operatore complementare. Se hai compilato il codice nel programma di esempio, vedrai che viene eseguito senza errori. Tuttavia, se hai provato ad aggiungere la riga:
cout << (a != b);
otterrete
errore del compilatore C2678 (MSVC): binario '! =': nessun operatore trovato che accetta un operando di sinistra di tipo 'Test' (o non c'è conversione accettabile) `.
Quindi, sebbene C ++ stesso non richieda il sovraccarico in coppia, non ti permetterà di usare un operatore di uguaglianza che non hai sovraccaricato su una classe personalizzata. È valido in .NET, perché tutti gli oggetti ne hanno uno predefinito; C ++ no.
1. Come nota a margine, lo standard C # richiede comunque di sovraccaricare la coppia di operatori se si desidera sovraccaricare uno dei due. Questa è una parte dello standard e non semplicemente il compilatore . Tuttavia, si applicano le stesse regole relative alla determinazione dell'operatore da chiamare quando si accede a una libreria .net scritta in un'altra lingua che non ha gli stessi requisiti.
2. EMCA-334 (pdf) ( http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-334.pdf )
3. E Java, ma non è questo il punto