:::: MENU ::::

Monday, July 16, 2012

There are many times in .NET where we have an instance of a value type that we need to treat as optional.  That is, we may want to consider its value as being supplied or missing.

The System.Nullable<T> structure in the .NET Framework can be used to represent these situations in a consistent and meaningful way.

Why Do We Use Nullable<T>?

With instances of reference types, you can easily denote an optional item by simply leaving the reference as null, but this isn’t really possible with a value type (that is, not directly), because the instance of a value type always has a value.

For example, if you had an Person class with some basic data:

   1: public class Person
   2: {
   3:     public string FirstName { get; set; }
   4:     public string LastName { get; set; }
   5:     public int YearsRetired { get; set; }
   6:  
   7:     // ...
   8: }

We could have a person with no first name (Sting, Madonna, etc… or is that no last name?), simply by setting the string property FirstName tonull:



   1: aPerson.FirstName = null;

But in the case of YearsRetired, if the person hasn’t retired yet, we can’t set a simple int to null, because an int is a value type, which must always have a value (strictly speaking):



   1: // compiler error
   2: aPerson.YearsRetired = null;

That said, we could use a sentinel value (-1), or have a separate bool field (IsRetired) to say whether we should use this field or not, but these get messy and harder to maintain.  Consider that if you use a sentinel value, everyone who uses this field must know what that value would be and to test for it.


Alternatively, if you use a bool field to tell you if the value field is usable, they aren’t encapsulated, so again there could be usage issues or consistency issues in naming, etc.


This is why Nullable<T> exists, to allow you to easily mark value type instances as optional (“nullable”) by providing a mechanism that gives values types something like null semantics in an encapsulated and uniform way.


In this way, anyone who looks at your interface and sees Nullable<int> (can also be abbreviated int?) will know how to test whether it has a valid value or not.


How the Nullable<T> struct Works


When you have a Nullable<T> wrapped type in the .NET Framework, it doesn’t give you a reference which you can make null.  It actually is a simple struct (value type) that wraps your value type.  This is an important distinction because it clears up some common misconceptions on what the Nullable<T> type does and does not do.


For example, let’s say you have:



   1: // or int? for short…
   2: Nullable<int> yearsRetired = null;

What really is yearsRetired?  Is it a reference that points to nothing?  No, it’s actually an instance of a value type with two fields: HasValue, and Value.  Note: for you C++ boost library users out there, this is much like how boost::optional<T> works.


The HasValue field is a bool that tells you whether or not Value contains a valid value, and the Value field contains the value set by the user.  Also, to make sure that you use the type correctly, if you attempt to access Value directly when HasValue is false, you will get anInvalidOperationException.


So as you can see, this mimics the behavior of a reference type in some ways, but not others.  For example, you won’t save any space having an “empty” Nullable<BigHonkingStruct>.  The Value field still has the space for a BigHonkingStruct, it’s just inaccessible (that is, it always has a value, it just may not be a valid – i.e. user assigned -- value).


This may be confusing, because while you think you are setting a field to a null, it’s really just compiler magic.  For example, you can do this:



   1: int? yearsRetired = null;
   2:  
   3: if (yearsRetired == null)
   4: {
   5:     Console.WriteLine(“Active Employee”);
   6: }

But this is just syntactical sugar that actually just converts the usage of null to mimic calls against HasValue and Value:



   1: int? yearsRetired = default(int?);     // creates with HasValue = false, Value = default(int)
   2:  
   3: if (yearsRetired.HasValue == false)
   4: {
   5:     Console.WriteLine(“Active Employee”);
   6: }

So don’t be fooled into thinking Nullable<T> magically saves space for “null” instances of large value types.  In fact, if you have a struct so large that you are worried about wasted space, consider a class instead (see C# Fundamentals: The Differences between Struct and Class for more details).


Getting a Default Value


Many times while you are using a Nullable<T> instance, you may find yourself writing code like this:



   1: int cost = 0;
   2:  
   3: if (contractSize.HasValue)
   4: {
   5:     // you can do math on Nullable<int> directly, with caveats…
   6:     cost = contractSize * price;
   7: }
   8:  
Which you could shorten down using a conditional, of course:

   1: int cost = (contractSize.HasValue ? contractSize.Value : 100) * price;

That is, you want to use the value of a Nullable<T> in an expression, or a stand-in if the instance is “null”.  Either way so far, it looks a wee bit ugly, but there are a few ways we can clean this code up.


Nullable<T> has a method GetValueOrDefault() that allows you to retrieve the value if it exists, or the default specified if not.  It has two forms:



  • GetValueOrDefault()

    • Returns Value if HasValue is true, or default(T) if not.

  • GetValueOrDefault(T defaultValue)

    • Returns Value if HasValue is true, or defaultValue if not.

Thus, the code we wrote above could more concisely be written as:



   1: int cost = contractSize.GetValueOrDefault(100) * price;

Ah, much cleaner!  In addition, if you want the defaultValue to be whatever the default is for the given type, you can just call it without any parameter.



   1: // these evaluate to same value, because 0 is default for int.
   2: int cost1 = contractSize.GetValueOrDefault(0) * price;
   3: int cost2 = contractSize.GetValueOrDefault() * price;  

Nullable<T> and the Null-coalescing operator


Another nice thing C# did in .NET 2.0 was to add a null-coalescing operator (??) to get the value of a reference type if non-null, or a stand-in value if null.  They also performed some syntactical candy to allow this to work with Nullable<T> as well.  Basically, this behaves very similarly to using GetValueOrDefault():



   1: Console.WriteLine(“ The contract size is: {0}”, contractSize ?? 100);

The main thing to note here is that ?? is very low on the operator precedence, so if instead you had typed this:



   1: // compiler error, thinks you are trying to ?? between string and int
   2: Console.WriteLine(“The contract size is: “ + contractSize ?? 100);

You’d get an error, because it first tries to concatenate the string and contractSize, which results in a string, and then attempts to null-coalesce a string with an int value, which is invalid.  That is, it thought you wanted this:



   1: // + has higher precedence than ??
   2: Console.WriteLine((“The contract size is: “ + contractSize) ?? 100);

So when you use ?? in an expression, make sure you surround it in parenthesis where appropriate to make sure it is performed in the order you really mean:



   1: Console.WriteLine(“The contract size is: “ + (contractSize ?? 100));

Nullable Math Doesn’t Always Add Up


Finally, there are some interesting results when you attempt to use an arithmetic or logical comparison operator overload on a Nullable<T>wrapping a T that has those operators.  That is, if you have:



   1: int? x = null;
   2:  
   3: if ((x * 5) < 100)
   4: {
   5:     // ...
   6: }
   7:  

What will the result be?  It turns out false because the operator * between null and 5 returns null, and null has no meaningful order so <returns false.  In essence, this is modeled to behave much like SQL expressions with null values.  The long and the short of the matter is that math with a null numeric type yields null, and an ordered logical comparison with a null yields false.


For more details, I have a post titled C#/.NET Little Pitfalls: Nullable Math Doesn’t Always Add Up which you can dig into for more information on why this happens.


Summary


The Nullable<T> is a handy structure that was created to give us a consistent way to handle “optional” instances of value types. Nullable<T>instances can be assigned to null or compared with null, which really is syntactical sugar which creates a default instance or checks theHasValue property respectively.


In addition, you can use the GetValueOrDefault() method or the null-coalescing operator (??) to query the value, or provide a substitute if the value was never set.


 


More