Подтвердить что ты не робот

Почему nullable bools не допускает, если (nullable), но разрешить if (nullable == true)?

Этот код компилируется:

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

Я думал, что если (booleanExpression == true) должно было быть избыточным. Почему это не так?

4b9b3361

Ответ 1

Нет никакого неявного преобразования от Nullable<bool> до bool. Существует неявное преобразование от bool до Nullable<bool> и что происходит (в терминах языка) с каждой из констант bool в первой версии. Затем применяется оператор bool operator==(Nullable<bool>, Nullable<bool>. (Это не совсем то же самое, что и другие поднятые операторы - результат равен bool, а не Nullable<bool>.)

Другими словами, выражение 'fred == false' имеет тип bool, тогда как выражение 'fred' имеет тип Nullable<bool>, поэтому вы не можете использовать его в качестве выражения if.

EDIT: для ответа на комментарии никогда не происходит неявного преобразования от Nullable<T> до T, и по уважительной причине - неявные преобразования не должны генерировать исключения, и если вы не хотите, чтобы null был неявно преобразован в default(T) не так много, что можно было бы сделать.

Кроме того, если бы неявные преобразования оба пути были округлены, выражение типа "nullable + nonNullable" было бы очень запутанным (для типов, которые поддерживают +, например, int). Будут доступны как + (T?, T?), Так и + (T, T), в зависимости от того, какой операнд был преобразован, но результаты могут быть самыми разными!

Я на 100% отстаю от решения только иметь явное преобразование от Nullable<T> до T.

Ответ 2

Потому что 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"; }   
   }

Ответ 3

Оператор Nullable<bool> == true неявно проверяет Nullable<bool> == (Nullable<bool>)true.

Обратите внимание, что Nullable<bool> сам по себе не является логическим. Это оболочка для булева, которая также может быть установлена ​​в значение null.

Ответ 4

Проблема реализации полностью заявлена, говоря: Fred имеет тип Nullable<bool>, а оператор ! не определен для Nullable<bool>. Нет причин, по которым оператор ! на Nullable<bool> должен быть определен в терминах bool.

Цитата Microsoft:

При выполнении сравнений с типами NULL, если один из nullable types имеет значение null, сравнение всегда оценивается как false.

В правиле не упоминается неявное преобразование. Это просто произвольное соглашение, которое призвано гарантировать, что никакие булевы выражения не имеют исключений. Как только правило будет на месте, мы знаем, как писать код. К сожалению, Microsoft упустила этот унарный оператор. Чтобы соответствовать поведению бинарных операторов, следующий код должен иметь счастливый конец.

Поэтому

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, в то время как Microsoft исправляет эту, казалось бы, последнюю ошибку.

Ответ 5

Если вы добавите fred в boolean, он скомпилирует:

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

Я думаю, что когда вы сравниваете bool? для bool, компилятор делает имплицитное литье, выполняет сравнение, а затем возвращает true или false. Результат: выражение оценивается как bool.

Когда вы не сравниваете bool? что-то, выражение оценивается в bool?, кто незаконно существует.

Ответ 6

Технически голый условный тест не требует неявного преобразования в 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