Почему nullable bools не разрешают if (nullable), но разрешают if (nullable == true)?

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

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) должен был быть избыточным. Почему не в этом случае?

36
задан AustinWBryan 11 August 2019 в 09:33
поделиться

5 ответов

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

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

РЕДАКТИРОВАНИЕ: Для ответа на комментарии никогда нет неявного преобразования от Nullable<T> до [1 110] и на серьезном основании - неявные преобразования не должны выдавать исключения, и если Вы не хотите null быть неявно преобразованными в [1 112] много еще нет, который мог быть сделан.

кроме того, если бы там были неявные преобразования оба окольных пути, выражение как "nullable + не допускающий NULL-значения" очень сбивало бы с толку (для типов, которые поддерживают +, как [1 113]). Оба + (T?, T?) и + (T, T) было бы доступно, в зависимости от которого операнда были преобразованы - но результаты могли очень отличаться!

я - 100% позади решения только иметь явное преобразование от [1 114] до [1 115].

59
ответ дан Jon Skeet 11 August 2019 в 19:33
поделиться
  • 1
    @Weston, Почему Вы говорите, что DialogFragment должен быть общедоступным статическим классом? Я использую DialogFragments все время и не имел потребности сделать их статичными... – IgorGanapolsky 21 August 2013 в 02:04

Поскольку fred не является булевской переменной. это - структура, которая имеет булево свойство под названием IsNull или HasValue, или безотносительно... Объект, названный fred, является сложным составным объектом, содержащим булевскую переменную и значение, не саму примитивную булевскую переменную...

Ниже, например, то, как мог быть реализован Интервал Nullable. Универсальный 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"; }   
   }
8
ответ дан Charles Bretana 11 August 2019 в 19:33
поделиться
  • 1
    @flock.dux: Все классы имеют значение по умолчанию, никакой (неявный) конструктор аргумента. Причина конструктор по умолчанию необходим, да, таким образом, это может инстанцировать платформа, но надлежащий способ инициализировать DialogFragment показывают в первом коде, Вы видите на этой странице developer.android.com/reference/android/app/DialogFragment.html с методом фабрики, который создает новый экземпляр и устанавливает аргументы в пользу фрагмента. ВЫ всегда будете первыми для инстанцирования фрагмента (использующий метод фабрики). Тогда в onCreate, Вы получаете доступ к аргументам, даже после экранного вращения (например.) – Jerry Destremps 28 February 2014 в 09:36

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

Примечание, что Nullable<bool> само не булевская переменная. Это - обертка для булевской переменной, которая может также быть установлена в NULL.

3
ответ дан lc. 11 August 2019 в 19:33
поделиться
  • 1
    этот класс со статическими участниками останется в памяти, пока classloader того класса жив, это займет время... – Eugene 27 May 2014 в 17:12

При кастинге fred к булевской переменной он скомпилирует:

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

я думаю это, когда Вы сравниваете bool? к bool компилятор делает бросок implicite, сделайте сравнение и затем возвратите TRUE или FALSE. Результат: выражение оценивает к bool.

, Когда Вы не сравниваете bool? к чему-то выражение оценивает к bool?, кто недопустим там.

0
ответ дан Sylvain Rodrigue 11 August 2019 в 19:33
поделиться

Технически простой условный тест не требует неявного преобразования в bool, если у вас есть реализация оператора true.

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

0
ответ дан 27 November 2019 в 05:46
поделиться
Другие вопросы по тегам:

Похожие вопросы: