Considérez le code suivant:
int? x = null;
Console.Write ("Hashcode: ");
Console.WriteLine(x.GetHashCode());
Console.Write("Type: ");
Console.WriteLine(x.GetType());
Une fois exécuté, il écrit que Hashcode est 0
, Mais échoue avec NullReferenceException
pour tenter de déterminer le type de x
. Je sais que les méthodes appelées sur des types nullables sont en fait appelées sur des valeurs sous-jacentes, donc je m'attendais à ce que le programme échoue pendant x.GetHashCode()
.
Alors, quelle est la différence fondamentale entre ces deux méthodes et pourquoi la première d'entre elles n'échoue-t-elle pas?
Ceci est dû au fait int? x = null;
crée essentiellement une instance de type valeur System.Nullable<int>
, avec une valeur "intérieure" null
(vous pouvez la vérifier via .HasVaue
Propriété). Lorsque GetHashCode
est invoqué, la substitution Nullable<int>.GetHashCode
est la méthode candidate (puisque la méthode est virtuelle), nous avons maintenant une instance de Nullable<int>
, et exécutez sa méthode d'instance, parfait.
Lors de l'appel de GetType
, la méthode n'est pas virtuelle, donc l'instance de Nullable<int>
est encadré dans System.Object
d'abord, selon le document , et la valeur encadrée est null
, d'où le NullReferenceException
.
Pour clarifier la bonne réponse de Danny Chen:
Nullable<T>
Est un type de valeur. Le type de valeur se compose d'un booléen, qui indique la nullité (faux signifie null) et d'un T, la valeur.Nullable<T>
Encadré. Ils encadrent soit un T
encadré, soit une référence nulle.S
est implémentée comme si elle avait un argument ref S
Invisible; c'est ainsi que this
est passé.C
est implémentée comme s'il y avait un argument C
invisible; c'est ainsi que this
est passé.Vous avez maintenant suffisamment d'informations pour déduire ce qui se passe. GetHashCode
est virtuel et remplacé par Nullable<T>
donc quand vous l'appelez, vous l'appelez comme s'il y avait un argument ref Nullable<T>
invisible pour this
. Aucune boxe ne se produit.
GetType
n'est pas virtuel et ne peut donc pas être remplacé et est défini sur object
. Par conséquent, il attend un object
pour this
. Lorsqu'il est appelé sur un Nullable<T>
, Le récepteur doit être encadré, et peut donc boxer sur null, et donc peut lancer.
Si vous appelez ((object)x).GetHashCode()
, Vous verriez une exception.
L'implémentation de Nullable<T>.GetHashCode()
est la suivante:
public override int GetHashCode()
{
if (!this.HasValue)
{
return 0;
}
return this.value.GetHashCode();
}
Ainsi, lorsque la valeur est nulle, elle vous donnera toujours 0
.
x.GetType()
est identique à null.GetType()
qui lancera Object reference not set to an instance of an object
On dirait que GetHashCode a une vérification nulle. (Utilisé JetBrains pour voir la défenition)
public override int GetHashCode()
{
if (!this.hasValue)
return 0;
return this.value.GetHashCode();
}