Devo sovraccaricare == Operatore?
-
08-07-2019 - |
Domanda
Come funziona l'operatore ==
in C #? Se viene utilizzato per confrontare oggetti della classe A , proverà ad abbinare tutte le proprietà di A o cercherà i puntatori nella stessa posizione di memoria (o forse qualcos'altro)?
Creiamo un esempio ipotetico. Sto scrivendo un'applicazione che utilizza l'API di Twitter e ha una classe Tweet , che ha tutte le proprietà di un singolo tweet: testo, mittente, data e ora, sorgente, ecc. Se voglio confrontare oggetti della classe Tweet per equivalenza, posso semplicemente usare:
Tweet a, b;
if (a == b)
{
//do something...
}
Questo verificherà l'equivalenza di tutte le proprietà della classe Tweet tra a e b ?
In caso contrario, l'approccio corretto sarebbe quello di sovraccaricare l'operatore ==
per verificare esplicitamente l'equivalenza di tutti i campi?
AGGIORNAMENTO: Dalle prime due risposte, ho ragione nel dare per scontato:
- Se l'operatore
==
o il metodo Equals non è sovraccaricato per una classe, l'operatore==
per l'oggetto viene utilizzata la classe. - L'operatore
==
per la classe oggetto verifica l'uguaglianza nella posizione della memoria. - Devo sovraccaricare l'operatore
==
o il metodo Equals per eseguire questo compito. - Nel sovraccarico, devo verificare manualmente l'equivalenza nelle proprietà, quindi non c'è modo di farlo in modo semi-automatico, diciamo, in un ciclo , giusto?
AGGIORNAMENTO # 2: Yuriy ha commentato che è possibile verificare l'equivalenza delle proprietà nell'operatore ==
con riflesso . Come si può fare? Potresti darmi qualche codice di esempio? Grazie!
Soluzione
MSDN ha un buon esempio di come farlo:
public override bool Equals(object o)
{
try
{
return (bool) (this == (DBBool) o);
}
catch
{
return false;
}
}
Quindi sovraccarichi == e! =:
// Equality operator. Returns dbNull if either operand is dbNull,
// otherwise returns dbTrue or dbFalse:
public static DBBool operator ==(DBBool x, DBBool y)
{
if (x.value == 0 || y.value == 0) return dbNull;
return x.value == y.value? dbTrue: dbFalse;
}
// Inequality operator. Returns dbNull if either operand is
// dbNull, otherwise returns dbTrue or dbFalse:
public static DBBool operator !=(DBBool x, DBBool y)
{
if (x.value == 0 || y.value == 0) return dbNull;
return x.value != y.value? dbTrue: dbFalse;
}
E non dimenticare di sovraccaricare il metodo GetHash.
Modifica:
Ho scritto il seguente esempio rapido per usare la riflessione in un confronto. Questo dovrebbe essere molto più completo, potrei provare a fare un blog su di esso se la gente lo vuole:
public class TestEquals
{
private int _x;
public TestEquals(int x)
{
this._x = x;
}
public override bool Equals(object obj)
{
TestEquals te = (TestEquals)obj;
if (te == null) return false;
foreach (var field in typeof(TestEquals)
.GetFields(BindingFlags.NonPublic | BindingFlags.Instance))
{
if (!field.GetValue(this).Equals(field.GetValue(te)))
return false;
}
return true;
}
}
Altri suggerimenti
Per i tipi di riferimento, le implementazioni predefinite sia dell'operatore ==
che del metodo Equals ()
controlleranno semplicemente che entrambi gli oggetti abbiano lo stesso riferimento e sono quindi la stessa istanza.
Se vuoi controllare che il contenuto di due diversi oggetti sia uguale, devi scrivere il codice per farlo da solo, in un modo o nell'altro. Sarebbe possibile avere a che fare con la riflessione (il MbUnit fa qualcosa di simile) ma con un pesante penalità prestazionali e una buona probabilità che non farebbe comunque quello che ti aspettavi, e dovresti implementare ==
o Equals
e GetHashCode
di mano.
L'approccio corretto è il sovraccarico del metodo uguale alla classe Tweet oltre all'operatore ==, come descritto qui .
Verificherà l'equivalenza di tutte le proprietà della classe Tweet tra aeb?
No
In caso contrario, l'approccio corretto sarebbe sovraccaricare l'operatore == per verificare esplicitamente l'equivalenza di tutti i campi?
Puoi sovraccaricare l'operatore == o sovraccaricare la funzione Uguali .
Modifica
@Yuriy ha dato un buon esempio per confrontare tutte le variabili non pubbliche. Dato che ho anche scritto un esempio, eccolo qui (il mio confronta le proprietà)
class TwitterItem
{
private string myValue = "default value";
public string Value1
{
get { return myValue; }
set { myValue = value; }
}
public string Value2
{
get { return myValue; }
set { myValue = value; }
}
public string Value3
{
get { return myValue; }
set { myValue = value; }
}
public override bool Equals(object obj)
{
if (base.Equals(obj)) return true;
Type type = typeof(TwitterItem);
PropertyInfo[] properties = type.GetProperties();
foreach (PropertyInfo property in properties)
{
if (false == property.GetValue(this, null).Equals(property.GetValue(obj, null)))
return false;
}
return true;
}
}
Puoi confrontare le proprietà usando reflection:
var a = new Entity() { Name = "test", ID = "1" };
var b = new Entity() { Name = "test", ID = "1" };
var c = new Entity() { Name = "test", ID = "2" };
System.Diagnostics.Debug.WriteLine(a.Equals(b));//Returns true
System.Diagnostics.Debug.WriteLine(a.Equals(c));//Returns false
public class Entity
{
public string Name { get; set; }
public string ID { get; set; }
public override bool Equals(object obj)
{
var t = obj.GetType();
foreach (var p in t.GetProperties())
{
if (t.GetProperty(p.Name).GetValue(obj, null) != t.GetProperty(p.Name).GetValue(this, null))
return false;
}
return true;
}
}