36 votes

Pourquoi les booléens nullables n'autorisent-ils pas if (nullable) mais permettent-ils si (nullable == true)?

Ce code compile:

     static void Main(string[] args)
    {
        bool? fred = true;

        if (fred == true)
        {
            Console.WriteLine("fred is true");
        }
        else if (fred == false)
        {
            Console.WriteLine("fred is false");
        }
        else
        {
            Console.WriteLine("fred is null");
        }
    }
 

Ce code ne compile pas .

     static void Main(string[] args)
    {
        bool? fred = true;

        if (fred)
        {
            Console.WriteLine("fred is true");
        }
        else if (!fred)
        {
            Console.WriteLine("fred is false");
        }
        else
        {
            Console.WriteLine("fred is null");
        }
    }
 

Je pensais que si (booleanExpression == true) était supposé être une redondance. Pourquoi n'est-ce pas dans ce cas?

61voto

Jon Skeet Points 692016

Il n'y a pas de conversion implicite de Nullable<bool> de bool. Il est une conversion implicite de bool de Nullable<bool> et c'est ce qui se passe (dans la langue) de chacun des bool constantes dans la première version. L' bool operator==(Nullable<bool>, Nullable<bool> de l'opérateur est alors appliquée. (Ce n'est pas tout à fait le même que les autres levé les opérateurs - le résultat est juste bool, pas Nullable<bool>.)

En d'autres termes, l'expression "fred == false' est de type bool, alors que l'expression "fred" est de type Nullable<bool> donc vous ne pouvez pas l'utiliser le "si" de l'expression.

EDIT: Pour répondre aux commentaires, il n'y a jamais une conversion implicite de Nullable<T> de T et pour une bonne raison - conversions implicites ne devrait pas lancer des exceptions, et, sauf si vous souhaitez null à être implicitement converti en default(T) il n'y a pas grand-chose d'autre qui pourrait en être fait.

Aussi, si il ont été conversions implicites à la fois des moyens de contourner, d'une expression comme "nullable + accepte pas les valeurs null" serait très déroutant (pour les types qui prennent en charge + comme int). Les Deux +(T?, T?) et +(T, T) seront disponibles, en fonction de la opérande ont été convertis, mais les résultats peuvent être très différents!

Je suis à 100% derrière la décision d'avoir seulement une conversion explicite de Nullable<T> de T.

8voto

Charles Bretana Points 59899

Parce que fred n'est pas un booléen. c'est une structure qui a une propriété booléenne appelée IsNull, ou HasValue, ou quoi que... L'objet nommé fred complexe est l'objet composite contenant une valeur de type boolean et une valeur, pas une primitive de type boolean lui-même...

Ci-dessous, par exemple, est de savoir comment un Nullable Int pourraient être mises en œuvre. Le générique Nullable est presque certainement mis en œuvre de même (mais de façon générique). Vous pouvez voir ici comment les conversions implicites et explicites sont mis en œuvre..

public struct DBInt
   {
       // The Null member represents an unknown DBInt value.
       public static readonly DBInt Null = new DBInt();
       // When the defined field is true, this DBInt represents a known value
       // which is stored in the value field. When the defined field is false,
       // this DBInt represents an unknown value, and the value field is 0.
       int value;
       bool defined;
       // Private instance constructor. Creates a DBInt with a known value.
       DBInt(int value) 
       {
              this.value = value;
              this.defined = true;
       }
       // The IsNull property is true if this DBInt represents an unknown value.
       public bool IsNull { get { return !defined; } }
       // The Value property is the known value of this DBInt, or 0 if this
       // DBInt represents an unknown value.
       public int Value { get { return value; } }
       // Implicit conversion from int to DBInt.
       public static implicit operator DBInt(int x) 
       { return new DBInt(x); }

       // Explicit conversion from DBInt to int. Throws an exception if the
       // given DBInt represents an unknown value.
       public static explicit operator int(DBInt x) 
       {
              if (!x.defined) throw new InvalidOperationException();
              return x.value;
       }
       public static DBInt operator +(DBInt x) 
       { return x; }
       public static DBInt operator -(DBInt x) 
       { return x.defined? -x.value: Null; }
       public static DBInt operator +(DBInt x, DBInt y) 
       {
              return x.defined && y.defined? 
                      x.value + y.value: Null;
       }
       public static DBInt operator -(DBInt x, DBInt y) 
       {
              return x.defined && y.defined?  
                      x.value - y.value: Null;
       }
       public static DBInt operator *(DBInt x, DBInt y) 
       {
              return x.defined && y.defined?  
                      x.value * y.value: Null;
       }
       public static DBInt operator /(DBInt x, DBInt y) 
       {
              return x.defined && y.defined?  
                     x.value / y.value: Null;
       }
       public static DBInt operator %(DBInt x, DBInt y) 
       {
              return x.defined && y.defined?  
                      x.value % y.value: Null;
       }
       public static DBBool operator ==(DBInt x, DBInt y) 
       {
              return x.defined && y.defined?  
                     x.value == y.value: DBBool.Null;
       }
       public static DBBool operator !=(DBInt x, DBInt y) 
       {
              return x.defined && y.defined?  
                     x.value != y.value: DBBool.Null;
       }
       public static DBBool operator >(DBInt x, DBInt y) 
       {
              return x.defined && y.defined?  
                     x.value > y.value: DBBool.Null;
       }
       public static DBBool operator <(DBInt x, DBInt y) 
       {
              return x.defined && y.defined?  
                     x.value < y.value: DBBool.Null;
       }
       public static DBBool operator >=(DBInt x, DBInt y) 
       {
              return x.defined && y.defined?  
                      x.value >= y.value: DBBool.Null;
       }
       public static DBBool operator <=(DBInt x, DBInt y) 
       {
              return x.defined && y.defined?  
                     x.value <= y.value: DBBool.Null;
       }
       public override bool Equals(object o) 
       {
              try { return (bool) (this == (DBInt) o); } 
              catch  { return false; }
       }
       public override int GetHashCode() 
       { return (defined)? value: 0; }   
       public override string ToString() 
       { return (defined)? .ToString(): "DBInt.Null"; }   
   }

3voto

lc. Points 50297

La déclaration Nullable<bool> == true vérifie implicitement Nullable<bool> == (Nullable<bool>)true .

Notez que Nullable<bool> n'est pas un booléen. C'est un wrapper pour un booléen qui peut également être défini sur null.

0voto

Arturo Hernandez Points 485

Le problème de l'application est parfaitement indiqué en disant: Fred est de type Nullable<bool> et de la ! opérateur n'est pas définie pour Nullable<bool>. Il n'y a aucune raison pour que l' ! (opérateur) Nullable<bool> devrait être défini en termes de bool.

Citant Microsoft:

Lors de la réalisation de comparaisons avec des types nullables, si l'un des les types nullables est null, la comparaison est toujours évalué à false.

La règle ne fait aucune mention de la conversion implicite. C'est juste une convention arbitraire qui a pour but de garantir qu'aucune des expressions Booléennes avoir des exceptions. Une fois que la règle est en place, nous savons comment écrire du code. Malheureusement, Microsoft a raté l'opérateur unaire. Pour être cohérent avec les opérateurs binaires comportement, le code suivant doit avoir une fin heureuse.

Donc

static void Main(string[] args)
{
    bool? fred = null;

    if (!fred)
    {
        Console.WriteLine("you should not see this");
    }
    else
    {
        Console.WriteLine("Microsoft fixed this in 4.5!!!");
    }
}

Je parie que vous il y a des programmeurs qui sont maintenant avoir à écrire fred==false alors que Microsoft corrige ce qui semble être le dernier problème de null.

0voto

SRO Points 1956

Si vous lancez fred en boolean, il compilera:

   if (( bool )fred )
      (...)
 

Je pense que quand tu compares bool? pour faire un bool, le compilateur fait un cast implicite, fait la comparaison, puis renvoie true ou false. Résultat: l'expression est évaluée à une valeur booléenne.

Quand vous ne comparez pas bool? à quelque chose, l'expression évaluer à un bool ?, qui est illégal là-bas.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X