为什么可空的bool不允许if(可为空)但允许if(nullable == true)?

时间:2009-01-15 16:17:34

标签: c# .net nullable

此代码编译:

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

此代码编译。

    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 个答案:

答案 0 :(得分:58)

没有从Nullable<bool>bool的隐式转换。 boolNullable<bool>的隐式转换,这就是第一个版本中每个bool常量发生的(用语言术语)。然后应用bool operator==(Nullable<bool>, Nullable<bool>运算符。 (这与其他提升的运算符不完全相同 - 结果只是bool,而不是Nullable<bool>。)

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

编辑:要回答评论,从来没有从Nullable<T>T的隐式转换,并且有充分的理由 - 隐式转换不应该抛出异常,除非您希望null被隐式转换为default(T)并没有其他可以做的事情。

另外,如果 隐式转换都是双向的,那么像“nullable + nonNullable”这样的表达式会非常混乱(对于支持+的类型,如int)。 +(T?,T?)和+(T,T)都可用,具体取决于转换的操作数 - 但结果可能非常不同!

我完全支持从Nullable<T>T的明确转换。

答案 1 :(得分:8)

因为fred不是布尔值。它是一个结构,它有一个名为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"; }   
   }

答案 2 :(得分:3)

声明Nullable<bool> == true隐含地检查Nullable<bool> == (Nullable<bool>)true

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

答案 3 :(得分:1)

完整地说明了实施问题:Fred属于Nullable<bool>类型,而!运算符未定义为Nullable<bool>。我们没有理由! Nullable<bool>上的bool运算符定义为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!!!"); } }

引用微软:

  

当与可空类型进行比较时,如果其中一个   可空类型为null,比较总是被评估为false。

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

因此

fred==false

我敢打赌,有些程序员现在必须编写{{1}},而Microsoft修复了这个看似最后一个空问题。

答案 4 :(得分:0)

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

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

我认为当你比较布尔时? bool,编译器做一个implicite强制转换,进行比较,然后返回true或false。结果:表达式评估为bool。

当你不比较布尔?某事,表达式评价为bool?,谁在那里非法。

答案 5 :(得分:0)

从技术上讲,如果你有一个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

中的类型提供了更多类似数据库的行为