TOC
Data types:

Nullable types

NULL literally means nothing - a variable that doesn't yet hold a value. At this point of the tutorial, you may have already dealt with variables which wasn't initialized, whether on purpose or not - if so, you also know that you always have to make sure that a variable has a value before you try to access this value. If not, you will likely be met with a NullReferenceException.

NULL's are especially relevant when dealing with your own objects and strings. On the other hand, when dealing with numbers, e.g. an integer, you will see that it always has a (default) value. For an integer, the default/fallback value is 0 (zero), which is never to be confused with NULL - they are not at all the same, even though some non-statically typed programming languages might treat them the same. 0 is a number - NULL is nothing.

So, if you ever find your self wanting to have a number which represents a non-defined/NULL value, you might feel stuck, because you can't assign null to an integer variable. Unless of course that integer is defined as nullable - a special language construct created for situations just like this. You define a nullable variable by post-fixing the type with a questionmark. Here's an example to illustrate the difference:

int notNullable = null; // Will cause an error from the compiler

int? nullable = null; // Just fine - it's nullable!

This is valid for all so-called value types like integers, floats, bools and structs. Strings and objects on the other hand are reference types and can't be declared as nullable, because they are, by default, nullable.

Checking a nullable for null

So, now that you have defined a variable which might be null, checking whether that is the case is obviously important. You can do this in two ways: Just compare the variable to the null kewyord, like you would for any other types, or use the HasValue property which a nullable object inherits from the System.Nullable struct. Here's an example:

int? nullable = null;
if (nullable == null)
    Console.WriteLine("It's a null!");
if (nullable.HasValue)
    Console.WriteLine("It's a null!");

The result is the same, so use whichever method you find most readable and easy to understand. Since our value can now be null, you should always check before using it - otherwise, you might run into an Exception.

Using the value of a nullable

From the System.Nullable, a nullable object also inherits the Value property. This can be used to retrieve the actual value of the nullable object. However, for simple comparison operations, e.g. using the == and the != operators, C# lets you omit the Value property and just directly compare the nullable object. In other words, both of these examples accomplish the same thing:

int? nullable = 42;

if (nullable.Value == 42)  
    Console.WriteLine("It's 42!");  

if (nullable == 42)  
    Console.WriteLine("It's 42!");

Nullable objects always come from a base data type, e.g. an integer as in the previous examples. While these data types may have a default value, the default value of a nullable is always null. That's why you have to check for null references before trying to use the value, as described previously. However, a nullable type inherits a very nice helper method which you can use: GetValueOrDefault(). It will return the value of the nullable object, unless it's null, in which case it will return the default value of the underlying type. So, for a nullable integer, it would return 0, for a nullable boolean it would return false and so on. This allows you to handle both the checking and the retrieval of the value in a single statement:

if ((nullable.HasValue) && (nullable.Value == 42))
    Console.WriteLine("It's 42!");

if(nullable.GetValueOrDefault() == 42)
    Console.WriteLine("It's 42!");

As you can see, the latter example is shorter and easier to read, while accomplishing the same thing.

Summary

Value types in C#, like integers and booleans, always have a default value. If you want to work around this behavior, e.g. because you need to differentiate between 0 and null, or because you need to know whether the user has actively selected "false" for something or if the variable just holds the default value of false, then you can make the variable nullable by postfixing the type with a ? (question mark).