Come dice il titolo: devo sostituire l'operatore ==
? che ne dici del metodo .Equals()
? Qualcosa che mi manca?Cosa deve essere sovrascritto in una struttura per garantire che l'uguaglianza funzioni correttamente?
risposta
Un esempio da MSDN
public struct Complex
{
double re, im;
public override bool Equals(Object obj)
{
return obj is Complex && this == (Complex)obj;
}
public override int GetHashCode()
{
return re.GetHashCode()^im.GetHashCode();
}
public static bool operator ==(Complex x, Complex y)
{
return x.re == y.re && x.im == y.im;
}
public static bool operator !=(Complex x, Complex y)
{
return !(x == y);
}
}
Per i curiosi: http://msdn.microsoft.com/en-us/library/336aedhh(v=VS.71).aspx – Mike
Mi chiedo se non sarebbe meglio per le prestazioni usare 'Complesso altro = obj come Complesso' e poi controlla se 'altro == null' invece di usare' is' e poi un cast ... –
@Clement: non puoi farlo per una struttura; il risultato non può essere nullo. Avresti un errore di compilazione. –
La differenza fondamentale tra i due è che l'operatore ==
è statico, cioè il metodo appropriato per invocare viene determinata in fase di compilazione, mentre il metodo Equals
viene richiamato dinamicamente su un esempio.
Definire entrambi è probabilmente la cosa migliore da fare, anche se ciò è meno importante nel caso delle strutture, poiché le strutture non possono essere estese (una struct non può ereditare da un'altra).
È inoltre necessario implementare IEquatable < T>. Ecco un estratto da Linee guida per la progettazione di framework:
DO implement IEquatable su tipi di valore. Il metodo Object.Equals sui tipi di valore causa la boxe e la sua implementazione predefinita non è molto efficiente perché utilizza il refection. IEquatable.Equals può offrire prestazioni molto migliori e può essere implementato in modo che non provochi il pugilato.
public struct Int32 : IEquatable<Int32> {
public bool Equals(Int32 other){ ... }
}
seguono le stesse linee guida per imperativi Object.Equals quando IEquatable.Equals di attuazione. Vedere la sezione 8.7.1 per dettagliate linee guida su imperativi Object.Equals
Quindi questo è usato solo sui tipi di valore? (non si fa riferimento?) – UpTheCreek
Poiché i tipi di riferimento non devono essere racchiusi tra loro quando vengono trasmessi come oggetto, ergo, IEquatable
Unfortunetely Non ho abbastanza fama di commentare altre voci. Quindi sto postando un possibile miglioramento per la soluzione migliore qui.
Correggetemi, se sbaglio, ma l'attuazione di cui sopra
public struct Complex
{
double re, im;
public override bool Equals(Object obj)
{
return obj is Complex && this == (Complex)obj;
}
public override int GetHashCode()
{
return re.GetHashCode()^im.GetHashCode();
}
public static bool operator ==(Complex x, Complex y)
{
return x.re == y.re && x.im == y.im;
}
public static bool operator !=(Complex x, Complex y)
{
return !(x == y);
}
}
Ha difetto principale. Sto riferendosi a
public override int GetHashCode()
{
return re.GetHashCode()^im.GetHashCode();
}
XOR è simmetrico, così complesso (2,1) e Complex (1,2) darebbe stesso hashCode.
probabilmente dovremmo fare qualcosa di più simile a:
public override int GetHashCode()
{
return re.GetHashCode() * 17^im.GetHashCode();
}
Avere collisioni hashcode non è necessariamente un problema. In effetti avrai sempre la possibilità di una collisione (leggi su buchi di pigione/paradosso di compleanno) Nel tuo caso Complesso (1,4) e Complesso (4,1) collidono (sicuramente ci sono state meno collisioni) dipende dai tuoi dati . Il codice hash viene utilizzato per estirpare rapidamente il 99,999% degli oggetti indesiderati (ad esempio, in un dizionario) Gli operatori di uguaglianza hanno l'ultima parola. – DarcyThomas
È stato detto che più proprietà hai sulla struttura, c'è una maggiore possibilità di collisione. Questo potrebbe essere un algoritmo di hash migliore: http://stackoverflow.com/a/263416/309634 – DarcyThomas
Solo per completezza vorrei anche consigliare al sovraccarico Equals
metodo:
public bool Equals(Complex other)
{
return other.re == re && other.im == im;
}
questo è un vero e proprio miglioramento spead come non c'è verificano boxe dell'argomento di input del metodo Equals(Object obj)
Alcune best practice per l'utilizzo dei tipi di valore:
- renderli immutabili
- Override Equals (quello che prende un oggetto come argomento);
- overload È uguale a prendere un'altra istanza dello stesso tipo di valore (ad esempio * Equals (Complex other));
- operatori di sovraccarico == e! =;
- esclusione GetHashCode
Questo deriva da questo post: http://theburningmonk.com/2015/07/beware-of-implicit-boxing-of-value-types/
maggior parte del tempo si può evitare Equals e GetHashCode di esecuzione le strutture - perché non v'è un'implementazione automatica dal compilatore per i tipi di valore utilizzando bit a bit contenuto + riflessione per i membri di riferimento.
Dai un'occhiata alla quel post:! Which is best for data store Struct/Classes?
Così, per la facilità d'uso si potrebbe ancora implementare == e =.
Ma la maggior parte delle volte è possibile evitare l'implementazione di Equals e GetHashcode.
Un caso in cui è necessario implementare Equals e GetHashCode è per un campo che non si vuole prendere in considerazione.
Ad esempio un campo che varia con il passare del tempo come Age of a Person o instantSpeed di un'auto (l'identità dell'oggetto non dovrebbe cambiare se si desidera ritrovarla nel dizionario nello stesso posto)
Cordiali saluti, codice migliore
Guarda anche http://stackoverflow.com/questions/1972262/c-sharp-okay-with-comparing-value-types-to-null/21962298 - se non lo sei attento quindi il confronto della tua struct (un tipo di valore) a null si compila bene ma non fa quello che ti aspetti. – yoyo