Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do nullable bools not allow if(nullable) but do allow if(nullable == true)?

Tags:

c#

.net

nullable

This code compiles:

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

This code does not compile.

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

I thought if(booleanExpression == true) was supposed to be a redundancy. Why isn't it in this case?

like image 960
Quibblesome Avatar asked Jan 15 '09 16:01

Quibblesome


People also ask

Can bools be null?

C# has two different categories of types: value types and reference types. Amongst other, more important distinctions, value types, such as bool or int, cannot contain null values.

How do you check if a Boolean is nullable?

(a ?: b) is equivalent to (if (a != null) a else b). So checking a nullable Boolean to true can be shortly done with the elvis operator like that: if ( a ?: false ) { ... } else { .... }

How check Boolean value is null or not in C#?

It be that you need to test if SessionManager is null before you access it, or maybe the HoldStringId needs to be tested for null, as well as that. Nullable types can be checked using the HasValue property, and their value can be retrieved with the Value property.

How do you declare a Boolean nullable in C#?

Declaration and assignmentm = m2; bool? flag = null; // An array of a nullable value type: int?[] arr = new int?[10]; The default value of a nullable value type represents null , that is, it's an instance whose Nullable<T>. HasValue property returns false .


2 Answers

There's no implicit conversion from Nullable<bool> to bool. There is an implicit conversion from bool to Nullable<bool> and that's what happens (in language terms) to each of the bool constants in the first version. The bool operator==(Nullable<bool>, Nullable<bool> operator is then applied. (This isn't quite the same as other lifted operators - the result is just bool, not Nullable<bool>.)

In other words, the expression 'fred == false' is of type bool, whereas the expression 'fred' is of type Nullable<bool> hence you can't use it as the "if" expression.

EDIT: To answer the comments, there's never an implicit conversion from Nullable<T> to T and for good reason - implicit conversions shouldn't throw exceptions, and unless you want null to be implicitly converted to default(T) there's not a lot else that could be done.

Also, if there were implicit conversions both ways round, an expression like "nullable + nonNullable" would be very confusing (for types that support +, like int). Both +(T?, T?) and +(T, T) would be available, depending on which operand were converted - but the results could be very different!

I'm 100% behind the decision to only have an explicit conversion from Nullable<T> to T.

like image 96
Jon Skeet Avatar answered Oct 13 '22 06:10

Jon Skeet


Because fred is not a boolean. it is a struct, which has a boolean property called IsNull, or HasValue, or whatever... The object named fred is the complex composite object containing a boolean and a value, not a primitive boolean itself...

Below, for example is how a Nullable Int could be implemented. The generic Nullable is almost certainly implemented similarly (but generically). You can see here how the implicit and explicit conversions are implemented..

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"; }       } 
like image 43
Charles Bretana Avatar answered Oct 13 '22 05:10

Charles Bretana