web-dev-qa-db-fra.com

Quand exactement les types nullables lèvent-ils des exceptions?

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.

34
Cheng Chen

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.
  • Contrairement à tous les autres types de valeur, les types nullables ne sont pas encadrés dans un Nullable<T> Encadré. Ils encadrent soit un T encadré, soit une référence nulle.
  • Une méthode implémentée par un type de valeur S est implémentée comme si elle avait un argument ref S Invisible; c'est ainsi que this est passé.
  • Une méthode implémentée par un type de référence C est implémentée comme s'il y avait un argument C invisible; c'est ainsi que this est passé.
  • Le cas intéressant est alors une méthode virtuelle définie dans une classe de base de référence et remplacée par une structure qui hérite de la classe de base.

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.

16
Eric Lippert

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

5
Sunil

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();
}
1
Captain0