Operatore di overloading == contro Equals ()

Sto lavorando a un progetto C # sul quale, fino ad ora, ho utilizzato oggetti e fabbriche immutabili per garantire che gli oggetti di tipo Foo possano sempre essere confrontati per l’uguaglianza con == .

Foo oggetti Foo non possono essere modificati una volta creati e la fabbrica restituisce sempre lo stesso object per un determinato set di argomenti. Funziona alla grande, e in tutto il codice base assumiamo che == sempre per controllare l’uguaglianza.

Ora ho bisogno di aggiungere alcune funzionalità che introducano un caso limite per il quale questo non sempre funziona. La cosa più semplice da fare è sovraccaricare l’ operator == per quel tipo, in modo che nessuno degli altri codici nel progetto debba essere modificato. Ma questo mi sembra un odore di codice: l’ operator == overload operator == e non Equals sembra strano, e io sono abituato alla convenzione che == controlla l’uguaglianza dei riferimenti, e Equals controlla l’uguaglianza degli oggetti (o qualunque sia il termine).

È una preoccupazione legittima, o dovrei semplicemente andare avanti e sovraccaricare l’ operator == ?

Credo che lo standard sia che per la maggior parte dei tipi, .Equals controlla la similarità dell’object, e operator == controlla l’uguaglianza di riferimento.

Credo che la best practice sia che per i tipi immutabili, l’operatore == dovrebbe verificare la similarità, oltre a .Equals . E se vuoi sapere se sono veramente lo stesso object, usa .ReferenceEquals . Vedi la class String C # per un esempio di questo.

C’è una grande differenza tra overload == e overriding Equals.

Quando hai l’espressione

 if (x == y) { 

Il metodo che verrà utilizzato per confrontare le variabili x e y è deciso al momento della compilazione . Questo è il sovraccarico dell’operatore. Il tipo usato quando si dichiara xey viene usato per definire quale metodo viene usato per confrontarli. Il tipo effettivo all’interno di xey (ovvero una sottoclass o implementazione di interfaccia) è irrilevante. Considera quanto segue.

 object x = "hello"; object y = 'h' + "ello"; // ensure it's a different reference if (x == y) { // evaluates to FALSE 

e il seguente

 string x = "hello"; string y = 'h' + "ello"; // ensure it's a different reference if (x == y) { // evaluates to TRUE 

Ciò dimostra che il tipo utilizzato per dichiarare le variabili xey viene utilizzato per determinare quale metodo viene utilizzato per valutare ==.

Per confronto, Equals viene determinato in fase di esecuzione in base al tipo effettivo all’interno della variabile x. Equals è un metodo virtuale su Object che altri tipi possono eseguire e sovrascrivere. Pertanto i seguenti due esempi valgono entrambi.

 object x = "hello"; object y = 'h' + "ello"; // ensure it's a different reference if (x.Equals(y)) { // evaluates to TRUE 

e il seguente

 string x = "hello"; string y = 'h' + "ello"; // ensure it's a different reference if (x.Equals(y)) { // also evaluates to TRUE 

Sente decisamente gli odori. In caso di sovraccarico == è necessario assicurarsi che sia Equals() che GetHashCode() siano coerenti. Vedere le linee guida MSDN .

E l’unica ragione per cui questo sembra OK è che descrivi il tuo tipo come immutabile.

Per i tipi immutabili non penso che ci sia qualcosa di sbagliato nell’avere sovraccarico == per supportare l’uguaglianza dei valori. Non penso di sovrascrivere == senza Equals a livello di override per avere comunque la stessa semantica. Se si esegue l’override == e è necessario controllare l’uguaglianza dei riferimenti per qualche motivo, è ansible utilizzare Object.ReferenceEquals(a,b) .

Vedi questo articolo di Microsoft per alcune linee guida utili

Esempio che mostra come implementarlo secondo le linee guida MSFT (sotto). Notare che quando si esegue l’override di Equals è necessario sovrascrivere GetHashCode (). Spero che questo aiuti la gente.

 public class Person { public Guid Id { get; private set; } public Person(Guid id) { Id = id; } public Person() { Id = System.Guid.NewGuid(); } public static bool operator ==(Person p1, Person p2) { bool rc; if (System.Object.ReferenceEquals(p1, p2)) { rc = true; } else if (((object)p1 == null) || ((object)p2 == null)) { rc = false; } else { rc = (p1.Id.CompareTo(p2.Id) == 0); } return rc; } public static bool operator !=(Person p1, Person p2) { return !(p1 == p2); } public override bool Equals(object obj) { bool rc = false; if (obj is Person) { Person p2 = obj as Person; rc = (this == p2); } return rc; } public override int GetHashCode() { return Id.GetHashCode(); } } 

Secondo le migliori pratiche di Microsofts, il risultato del metodo Equals e il sovraccarico di equals (==) dovrebbero essere gli stessi.

CA2224: l’override equivale a un operatore di overload uguale a