36

此代码编译:

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)应该是冗余。为什么不在这种情况下?

4

6 回答 6

60

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

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

编辑:为了回答评论,从来没有隐式转换从Nullable<T>toT并且有充分的理由 - 隐式转换不应该抛出异常,除非你想null被隐式转换为default(T),否则没有什么可以做的。

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

我 100% 支持仅从 to 进行显式转换的Nullable<T>决定T

于 2009-01-15T16:21:24.317 回答
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"; }   
   }
于 2009-01-15T16:20:40.823 回答
3

该语句Nullable<bool> == true隐式检查Nullable<bool> == (Nullable<bool>)true.

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

于 2009-01-15T16:24:10.737 回答
0

如果你将 fred 转换为布尔值,它将编译:

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

我认为当你比较布尔?bool,编译器进行隐式转换,进行比较,然后返回真或假。结果:表达式计算为布尔值。

当你不比较布尔?对于某事,表达式计算为布尔值?,谁在那里是非法的。

于 2009-01-15T16:26:32.463 回答
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

最初的问题是寻找一种 SQL 风格的 null 实现,其中 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

于 2009-07-29T20:48:32.853 回答
0

实现问题完美地表述为:Fredis of type Nullable<bool>and the !operator is not defined for Nullable<bool>。没有理由说明!运算符 onNullable<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!!!");
    }
}

我敢打赌,有些程序员现在不得不fred==false在微软修复这个看似最后一个空问题的同时编写代码。

于 2011-09-09T23:34:13.723 回答