Nullable <int> contre int? - Y a-t-il une différence?

93

Apparemment Nullable<int>et int?sont équivalents en valeur. Y a-t-il des raisons de choisir l'un plutôt que l'autre?

Nullable<int> a = null;
int? b = null;
a == b; // this is true
Zachary Scott
la source

Réponses:

135

Aucune différence.

int?est juste un raccourci pour Nullable<int>, qui lui-même est un raccourci pour Nullable<Int32>.

Le code compilé sera exactement le même quel que soit celui que vous choisissez d'utiliser.

LukeH
la source
1
Malheureusement, dans certains cas, ce n'est pas strictement vrai. Voyez cette réponse .
qqbenq le
22

Le ?formulaire n'est qu'un raccourci pour le type complet. La préférence personnelle est la seule raison de choisir l'un plutôt que l'autre.

Détails complets ici .

La syntaxe T?est un raccourci pour Nullable<T>, où Test un type valeur. Les deux formes sont interchangeables.

Steve Townsend
la source
18

Bien que je sois tout à fait d'accord pour dire que dans la plupart des cas, ils sont identiques, je suis récemment tombé sur une situation où il y a une différence entre les deux. Pour les détails sanglants, voyez cette question , mais pour vous donner un exemple rapide ici:

void Test<T>(T a, bool b)
{
    var test = a is int? & b;              // does not compile
    var test2 = a is Nullable<int> & b;    // does compile
}

La première ligne donne les messages d'erreur suivants:

error CS1003: Syntax error, ':' expected 
error CS1525: Invalid expression term ';'

Si vous êtes curieux de connaître la raison exacte de cela, je vous recommande vraiment de vérifier la question déjà liée , mais le problème de base est que dans la phase d'analyse après un is(ou un as) opérateur, lorsque nous sommes confrontés à un ?jeton, nous vérifions si le suivant le jeton peut être interprété comme un opérateur unaire ( &pourrait en être un) et si c'est le cas: l'analyseur ne se soucie pas de la possibilité que le ?jeton soit un modificateur de type, il utilise simplement le type avant lui et analysera le reste comme si le ?token était un opérateur ternaire (ainsi l'analyse échouera).

Ainsi, bien qu'en général int?et qu'ils Nullable<int>soient interchangeables, il y a des cas secondaires où ils produisent des résultats complètement différents, en raison de la façon dont l'analyseur voit votre code.

qqbenq
la source
2
Le premier cas test,, est fixé avec une parenthèse, donc var test = (a is int?) & b;. Peut également être fixé avec var test = a is int? && b;, et étant donné que best un paramètre de valeur simple (pas d'effets secondaires sur l' évaluation) , il semble étrange de préférer &plus &&.
Jeppe Stig Nielsen
Dans cette syntaxe particulière, ?est un caractère clé.
Pete Garafano le
Voir la question et la réponse liées, vos commentaires y sont tous adressés :) Je viens d'ajouter cette information ici, car je l'ai jugée pertinente, car elle fait ressortir une différence entre les deux formes, et prouve qu'il ne s'agit pas simplement de sucre syntaxique
qqbenq
Jeppe a raison. Il ne compile pas car il est interprété int?comme une opération ternaire ( var test = a is int? <return if true> : <return if false>), pas comme un int nullable.
Levi Fuller
1
@LeviFuller (suite) Consultez la documentation de ces boolsurcharges particulières . Ce sont des opérateurs logiques booléens (le &for bool) et des opérateurs logiques conditionnels booléens (le &&for bool). Notez comment la première de ces sous-sections est clairement nommée logique . N'oubliez pas qu'en C #, il n'y a pas de conversions ("casts") entre boolles types numériques et!
Jeppe Stig Nielsen
6

Il y a apparemment une différence entre les deux lors de l'utilisation de la génération Entity Framework (EF) avec code first:

Lorsque votre entité contient une propriété déclarée comme:

public class MyEntity
{
    public Nullable<int> MyNullableInt { get; set; } 
}

EF ne générera pas de propriété nullable et vous devrez forcer le générateur à le rendre nullable comme ceci:

public class YourContext : DbContext
{
    public DbSet<MyEntity> MyEntities{ get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Entity<MyEntity>().Property(x => x.MyNullableInt).IsOptional();
    }
}

D'autre part, si vous déclarez votre entité comme:

public class MyEntity
{
     public int? MyNullableInt { get; set; }
}

le générateur EF générera une propriété Nullable avec un champ Nullable dans la table de base de données correspondante.

Maciej
la source
2
C'est vraiment dommage.
siride
8
Cela suggère que vous avez une autre Nullabledéfinition quelque part, car avec la fonction intégrée Nullable<T>, il n'est pas possible pour EF de voir la différence entre les deux. Même si les gens d'EF voulaient les traiter différemment, ils ne le pouvaient pas.
1
Quelqu'un a-t-il confirmé que c'était le cas? (juste un peu prudent à cause des votes négatifs)
RayLoveless
@RayL Non, ce n'est pas le cas. Cette réponse n'est pas correcte et, comme l'a souligné hvd, impossible.
Chaussure
1
Étant donné que le code EF est généré à l'aide de modèles de code, cela peut en fait être possible. Ma réponse est basée sur mon expérience et le changement que j'ai suggéré a résolu le problème que j'avais. En outre, il semble que certaines personnes aient trouvé que c'était effectivement le cas sur la base des votes positifs.
Maciej
2

Nullable est un type générique, mais int? n'est pas.

Il existe certains scénarios où Nullable doit être utilisé sur int?

par exemple: ici vous ne pouvez pas remplacer Nullable par int?

comment pouvez-vous changer le code ci-dessous sans utiliser Nullable ?

class LazyValue<T> where T : struct
{
   private Nullable<T> val;
   private Func<T> getValue;

   // Constructor.
   public LazyValue(Func<T> func)
   {
      val = null;
      getValue = func;
   }

   public T Value
   {
      get
      {
         if (val == null)
            // Execute the delegate.
            val = getValue();
         return (T)val;
      }
   }
}
Rajes
la source
3
Ce qui peut avoir son importance si la question se pose sur les types génériques, mais la question a spécifié int, il est donc pas Nullable<T>lui estNullable<int>
Andrew