代码之家  ›  专栏  ›  技术社区  ›  Quibblesome

为什么可以为null的布尔值不允许if(可以为null),但允许if(可为null==true)?

  •  36
  • Quibblesome  · 技术社区  · 16 年前

    此代码编译:

    private 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");
    }
    

    此代码确实 编译。

    private 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");
    }
    

    我以为 if(booleanExpression == true) 本来应该是裁员。为什么不是这种情况?

    6 回复  |  直到 6 年前
        1
  •  60
  •   Jon Skeet    16 年前

    没有隐式转换 Nullable<bool> bool 在那里 隐式转换 布尔 可为空<bool> 这就是第一个版本中每个bool常量的情况(用语言术语来说)。这个 bool operator==(Nullable<bool>, Nullable<bool> 然后应用运算符。(这与其他起重操作员不太一样——结果只是 布尔 ,不 可为空<bool> .)

    换句话说,表达式“fred==false”的类型为 布尔 ,而表达式“fred”的类型为 可为空<bool> 因此,您不能将其用作“if”表达式。

    编辑:要回答评论,从来没有隐式的转换 Nullable<T> T 并且有充分的理由-隐式转换不应该抛出异常,除非你愿意 null 隐式转换为 default(T) 没有多少别的办法了。

    此外,如果有 双向隐式转换,像“nullable+nonNullable”这样的表达式会非常令人困惑(对于支持+的类型,如 int ).根据转换的操作数,+(T?,T?)和+(T,T)都是可用的,但结果可能会大不相同!

    我100%支持只进行明确转换的决定 可为空<T> T .

        2
  •  8
  •   Charles Bretana    12 年前

    因为弗雷德不是布尔值。它是一个结构体,具有一个名为IsNull、HasValue或其他任何布尔属性。..名为fred的对象是包含布尔值和值的复杂复合对象,而不是原始布尔值本身。..

    例如,下面是如何实现Nullable Int。几乎可以肯定,泛型Nullable的实现方式是类似的(但是通用的)。您可以在这里看到隐式和显式转换是如何实现的。。

    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"; }   
       }
    
        3
  •  3
  •   lc.    16 年前

    声明 Nullable<bool> == true 正在隐式检查 Nullable<bool> == (Nullable<bool>)true .

    注意 Nullable<bool> 它本身不是布尔值。它是一个布尔值的包装器,也可以设置为null。

        4
  •  0
  •   Sylvain Rodrigue    16 年前

    如果你将fred转换为boolean,它将编译:

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

    我认为当你比较布尔时?对于bool,编译器进行隐式转换,进行比较,然后返回true或false。结果:表达式的计算结果为bool。

    当你不比较bool时?对于某物,表达式的计算结果为bool?谁在那里是非法的。

        5
  •  0
  •   Mike Harkavy Mike Harkavy    16 年前

    从技术上讲,如果你有true运算符的实现,那么裸条件测试不需要隐式转换为bool。

    bool? nullableBool = null;
    SqlBoolean sqlBoolean = SqlBoolean.Null;
    bool plainBool = sqlBoolean; // won't compile, no implicit conversion
    if (sqlBoolean) { } // will compile, SqlBoolean implements true operator
    

    最初的问题是寻找null的SQL风格实现,其中null更像是一个未知值,而Nullable实现更像是添加null作为额外的可能值。例如,比较:

    if (((int?)null) != 0) { } //block will execute since null is "different" from 0
    if (SqlInt32.Null != 0) { }  // block won't execute since "unknown" might have value 0
    

    中的类型提供了更多类似数据库的行为 System.Data.SqlTypes

        6
  •  0
  •   Arturo Hernandez    14 年前

    实施问题可以完美地表述为: Fred 属于类型 Nullable<bool> 以及 ! 未为定义运算符 可为空<bool> 。没有理由 ! 操作员打开 可为空<bool> 应该从以下方面来定义 bool .

    引用微软的话:

    在与可以为null的类型进行比较时,如果 可以为null的类型为null,比较结果总是为false。

    该规则没有提到隐式转换。这只是一个任意的约定,旨在保证没有布尔表达式有异常。一旦规则到位,我们就知道如何编写代码。遗憾的是,微软错过了这个一元运算符。为了与二进制运算符的行为保持一致,以下代码应该有一个圆满的结局。

    因此

    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!!!");
        }
    }
    

    我敢打赌,现在有程序员不得不写作 fred==false 而微软修复了这个看似最后一个空问题。