#1,199 – Equality and Inequality with Nullable Types

The equality and inequality operators work with nullable types as follows:

  • If both operands are non-null, the operators are applied to the values
  • If one operand is null and the other has a value, == returns false, != returns true
  • If both operands are null, == returns true and != returns false
            int? val1 = 5;
            int? val2 = 10;
            int? val3 = null;
            int? val4 = null;
            int? val5 = 10;

            bool b1 = val1 == val2;   // False
            b1 = val2 == val5;        // True
            b1 = val1 == val3;        // False
            b1 = val1 != val3;        // True
            b1 = val3 == val4;        // True
            b1 = val3 != val4;        // False
Advertisement

#1,198 – Using Operators with Nullable Types

When using a nullable type, you can use the operators with the nullable type that are associated with the underlying value type.

The example below shows how we can use the < and > operators with the int? type.  They work as they would work with the int type itself, for non-null values.

            int? val1 = 5;
            int? val2 = 10;

            bool b1 = val1 < val2;   // True

If one of the two values is null, the expression will evaluate to false, regardless of the other value.

            val2 = null;
            b1 = val1 < val2;    // False
            b1 = val2 < val1;    // False
            b1 = val1 == val2;   // False

#776 – Declaring and Using Nullable structs

You can make any value type nullable, allowing it to either be null or to contain one of its normal values.

Since user-defined structs are value types, you can also make any struct nullable, using Nullable<T> or T? notation.

            // Regular struct
            GuyInfo gi1 = new GuyInfo("Isaac", 1642);

            // Nullable, with no value
            Nullable<GuyInfo> gi2 = null;

            // Nullable, 2nd form, with value
            GuyInfo? gi3 = new GuyInfo("Albert", 1879);

            bool hasVal1 = gi2.HasValue;
            bool hasVal2 = gi3.HasValue;

776-001

#582 – Use the as Operator to Unbox to a Nullable Type

You can box regular value types or their equivalent nullable types (e.g. int and int?) and the boxed values will either be null or be of the underlying value type.

You can unbox these values to a nullable type, or use the as operator to do the unboxing.  The example below shows the result of unboxing several different values to a nullable int (int?) using the as operator.

int? i1 = null;   // Nullable<int> w/no value
int? i2 = 42;     // Nullable<int> with a value
int i3 = 12;      // Plain old int

// Boxing nullable types
object o1 = i1;
object o2 = i2;
object o3 = i3;
object o4 = new Dog("I'm not an int", 12);

// Unboxing to nullable types
int? ia1 = o1 as int?;    // null
int? ia2 = o2 as int?;    // 42
int? ia3 = o3 as int?;    // 12
int? ia4 = o4 as int?;    // null

bool bHasVal = ia1.HasValue;  // false
bHasVal = ia2.HasValue;       // true
bHasVal = ia3.HasValue;       // true
bHasVal = ia4.HasValue;       // false

#217 – T? Is Equivalent to Nullable<T>

You can always use the form T?, rather than Nullable<T>, to declare a variable whose type is a nullable value type.  The type T can be any built-in or custom value type.

This means that you can use this syntax for your own custom enum or struct types.

For example, if you have the following custom types:

        public enum Mood
        {
            Crabby,
            Happy,
            Petulant,
            Elated
        }

        public struct Point3D
        {
            public float X, Y, Z;
            public string Name;
            public Point3D(float x, float y, float z, string name)
            {
                X = x;
                Y = y;
                Z = z;
                Name = name;
            }
        }

You can use the T? format as follows:

            Mood? myMood = null;
            Mood mood2 = myMood ?? Mood.Elated;

            Point3D? somePoint = null;
            Point3D defaultPoint = new Point3D(0.0f, 0.0f, 0.0f, "Origin");
            Point3D aPoint = somePoint ?? defaultPoint;

#216 – Null-Coalescing (??) Operator Is Equivalent to GetValueOrDefault Method

The Nullable<T> type has a GetValueOrDefault(T) method that behaves in a way identical to the ?? operator.  It returns the value of the object, if non-null, or the value of the parameter passed in if the object is null.

            Nullable<Mood> myMood = null;

            Mood mood2 = myMood ?? Mood.Happy;   // result = Happy, since myMood is null
            Mood mood3 = myMood.GetValueOrDefault(Mood.Happy);   // same as above

There is also an overload of the GetValueOrDefault method that doesn’t take a parameter but just returns the default value for the type T, if the object in question is null.  For example, an enum type has a default value of 0, so the enumerated constant equivalent to 0 is returned.

        public enum Mood
        {
            Crabby,   // 0-valued
            Happy,
            Petulant,
            Elated
        }
            Nullable<Mood> myMood = null;

            Mood mood2 = myMood.GetValueOrDefault();   // Gets value Mood.Crabby

#215 – Using the Null-Coalescing (??) Operator with Custom Nullable Types

In addition to the built-in nullable types, you can also use the null-coalescing operator on custom nullable types using Nullable<T>.

Here’s an example:

            Nullable<Mood> myMood1 = null;
            Nullable<Mood> myMood2 = Mood.Petulant;

            Mood mood3 = myMood1 ?? Mood.Happy;   // result = Happy
            Mood mood4 = myMood2 ?? Mood.Happy;   // result = Petulant

#214 – Using the Null-Coalescing (??) Operator with Reference Types

The ?? operator is often used with nullable value types, assigning a value to an equivalent non-nullable type (e.g. int? to int).  But it can also be used with reference types.

The behavior with reference types is the same as with value types–the value of the expression is equal to the first operand, if non-null, or the second operand if the first operand is null.

Here’s an example:

            Person favArtist = new Person("Connee", "Boswell");
            Person oldBlueEyes = new Person("Frank", "Sinatra");

            Person buyCDOf = favArtist ?? oldBlueEyes;  // Frank is our fallback

In this example, buyCDOf will be assigned the Connee Boswell Person object.  But if the favArtist variable had been null, we would have defaulted to assigning buyCDOf to Frank Sinatra.

#211 – Using the Null-Coalescing (??) Operator

In C#, the ?? operator, know as the null-coalescing operator, is often used with nullable types.

The ?? operator is used in an expression of the form:

            operand ?? nullval

This expression evaluates to:

  • operand, if operand is non-null
  • nullval, if operand is null

The operand is a variable and the nullval is a variable or constant of the same type as the variable.

The ?? operator is generally used where you have a nullable type and you want to assign its value to a non-nullable type.  Since the nullable type can have the value of null, you need to specify what non-null value to assign in that case.

Here’s an example:

            uint? age = null;   // Nullable age -- might not have a value

            // Later: assign to a non-nullable uint.
            //   Store the age (if non-null)
            //   Store 0  (if null)
            uint ageStore = age ?? 0;

#210 – Checking to See Whether a Nullable Object Has a Value

Both the built-in nullable types (e.g. int?) and any Nullable<T> types that you use have a HasValue property that lets you determine whether a particular nullable object has a value or not.

  • HasValue is true if the object has a value of the corresponding type
  • HasValue is false if the object has a null value

For example, if you are using a nullable int:

            int? herAge = null;

And you later want to see if this variable has a value (of type int), you can do this:

            if (herAge.HasValue)
                Console.WriteLine("She is {0} yrs old", herAge);

HasValue is useful in cases when you want to perform an operation on an object, where the operation is only valid if the object is non-null.