Nullable.Equals(object) … comportamento “strano” che ha una spiegazione !

Ieri, in ufficio con alcuni miei colleghi, mi sono imbattuto in un comportamento alquanto strano del metodo Equals(object) della struct Nullable<T>.

Consideriamo il seguente frammento di codice :

short? x = 1;
bool b = x.Equals(1);

Secondo voi….quanto vale la variabile b ? Ebbene….magicamente false !

A questo punto consideriamo il seguente frammento di codice :

short? x = 1;
// caso 1
bool b = x.Equals(1);
// caso 2
b = x.Equals((short)1);
// caso 3
short y = 1;
b = x.Equals(y);

Il “caso 1” è ovviamente quello appena visto e ritorna “stranamente” b uguale a false. Il “caso 2” e “caso 3” si comportano correttamente fornendo un valore true.

Cambiamo il “caso 1” nel modo seguente :

int? x = 1;
bool b = x.Equals(1);

In questo caso, abbiamo semplicemente modificato la dichiarazione del nullable type dashort? a int? ed il valore di b diventa “magicamente” true !

Ecco che a questo punto, dotandomi di Reflector ho cominciato ad indagare sull’implementazione del metodo Equals(object) della struct Nullable<T> che riporto di seguito.

public override bool Equals(object other)
{
    if (!this.HasValue)
    {
        return (other == null);
    }
    if (other == null)
    {
        return false;
    }
    return this.value.Equals(other);
}

Dopo aver eseguito un paio di controlli preliminari sul fatto che la struct abbia un valore e che l’oggetto passato per il confronto non sia null, viene invocato il metodo Equals(object) del tipo T del campo value (interno alla struct Nullable<T>).

Considerando il caso in cui avevamo la dichiarazione short?, ossia Nullable<short>, il metodo che viene invocato sarà ovviamente Int16.Equals(object) la cui implementazione è la seguente :

public override bool Equals(object obj)
{
    return ((obj is short) && (this == ((short) obj)));
}

L’utilizzo dell’operatore is, per la verifica del tipo del parametro, svela l’arcano !

Come sappiamo, tutti i numeric literals (il numero “1” nel nostro caso) sono trattati implicitamente come Int32, per cui la chiamata x.Equals(1) (dove x è di tipo short?) restituisce “correttamente” false, in quanto obj non sarà short ma int !

In tutti gli altri casi, dichiarando esplicitamente una variabile di tipo short (si veda short y = 1) oppure eseguendo il cast esplicito a short del numeric literal (si veda x.Equals((short)1)), forziamo il tipo (coincidente con short? x) ed il confronto ritorna un valore corretto.

Questo spiega anche il perchè del fatto che modificando la dichiarazione da short? x a int? x, otteniamo un valore corretto per x.Equals(1)….in quanto il numeri literal “1” come già detto è implicitamente un Int32 !

In conclusione…..attenzione all’uso del metodo Equals(object) e dei numeric literals nei confronti !!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s