C# Nullable Examples

Examine nullable types, including nullable ints and DateTimes. Test the memory usage of nullables.
Nullable. Imagine a world where every value type, like int, could be null. We live in this world. We must wrap an int inside a nullable struct, with int?, to have a null int.Null
Notes, syntax. Nullable types are constructed by specifying the question mark after a value type in a declarator statement. The nullable int is specified with the syntax "int?".Int, uint
Int example. A nullable int is specified with the "int?" syntax. We can use a question mark (at the end of a value type) to transform the type into a nullable type.

Here: We see a null int type, the HasValue and Value properties. We use the nullable type to acquire the value of the instance.


Info: The int type is aliased to System.Int32—using "System.Int32?" would work as well.

Using System

HasValue: This property returns a bool that indicates whether the nullable instance contains a set value.


Value: If the HasValue property is true, you can access the Value property without an exception.

C# program that uses nullable int type using System; class Program { static void Main() { // // Create a local variable of type nullable integer. // ... It is initially assigned to null. // ... The HasValue property is false. // int? value = null; Console.WriteLine(value.HasValue); // // Assign the nullable integer to a constant integer. // ... The HasValue property is now true. // ... You can access the Value property as well. // value = 1; Console.WriteLine(value.HasValue); Console.WriteLine(value.Value); Console.WriteLine(value); if (value == 1) { Console.WriteLine("True"); } } } Output False True 1 1 True
Bool example. To use a nullable bool, use the type "bool?" with the trailing question mark. This is a struct that contains a bool. The "bool?" can be set to null, true and false.True, False

Also: The program shows that each "bool?" occupies 2 bytes in memory. It has an extra byte of overhead beyond a regular bool.


Info: This syntax enables us to represent a null, true and false value in a single variable.

C# program that uses nullable bool using System; class Program { static void Main() { bool? tristate = null; tristate = true; tristate = false; Console.WriteLine(tristate); long m1 = GC.GetTotalMemory(false); bool?[] b1 = new bool?[100000]; long m2 = GC.GetTotalMemory(false); b1[0] = false; Console.WriteLine("{0} bytes per bool?", (m2 - m1) / 100000); } } Output False 2 bytes per bool?
Tri-state enum. We consider a tri-state enum, which can be implemented with a byte backing store. Notice how the semicolon syntax ": byte" is used after the enum type declaration.

Info: The enum can be set to Tristate.Null, Tristate.True and Tristate.False. It works like any other enum.


And: Unlike the nullable bool, all 3 values can be represented in one byte of storage.


Benefit: The enum type avoids the overhead associated with wrapping a value type in a generic struct.

Generic Class, Method
C# program that uses Tristate byte enum using System; class Program { enum Tristate : byte { Null = 0, True = 1, False = 2 } static void Main() { Tristate tristate = Tristate.Null; tristate = Tristate.True; tristate = Tristate.False; Console.WriteLine(tristate); long m1 = GC.GetTotalMemory(false); Tristate[] b1 = new Tristate[100000]; long m2 = GC.GetTotalMemory(false); b1[0] = Tristate.False; Console.WriteLine("{0} byte(s) per Tristate", (m2 - m1) / 100000); } } Output False 1 byte(s) per Tristate
DateTime example. DateTime is a struct, so it cannot directly be null, but we can have a nullable DateTime wrapper. We use the question mark syntax, which results in the type "DateTime?".DateTime

Tip: A "DateTime?" is a struct that wraps a DateTime struct, providing another level of indirection that can simplify some programs.

Main: Here the "DateTime?" variable is declared. It is passed as a parameter to the Test method, and is assigned to different values.

HasValue: This returns true if the nullable type is logically non-null, and false if the null field on the nullable type is activated.

Also: The GetValueOrDefault method will return DateTime.MinValue for a null DateTime.

C# program that uses null DateTime struct using System; class Program { static void Main() { // // Declare a nullable DateTime instance and assign to null. // ... Change the DateTime and use the Test method. // DateTime? value = null; Test(value); value = DateTime.Now; Test(value); value = DateTime.Now.AddDays(1); Test(value); // // You can use the GetValueOrDefault method on nulls. // value = null; Console.WriteLine(value.GetValueOrDefault()); } static void Test(DateTime? value) { // // This method uses the HasValue property. // ... If there is no value, the number zero is written. // if (value.HasValue) { Console.WriteLine(value.Value); } else { Console.WriteLine(0); } } } Output 0 9/29/2009 9:56:21 AM 9/30/2009 9:56:21 AM 1/1/0001 12:00:00 AM
Nullable struct. We can directly access the Nullable generic struct. Here we use a Nullable int by specifying the Nullable type. This is the same thing as int?, but more verbose.
C# program that uses Nullable generic struct using System; class Program { static void Main() { // Use Nullable directly. Nullable<int> test = 100; if (test.HasValue) { Console.WriteLine("HAS VALUE: {0}", test.Value); } // Set Nullable int to null. test = null; if (test.HasValue) { Console.WriteLine("NOT REACHED"); } } } Output HAS VALUE: 100
Memory usage. This program calculates the memory usage for allocating a large array of nullable integers. You can declare a nullable type array using int?[] as the type.Int Array

Here: We measure the memory usage in the managed heap before and after allocating an array of nullable ints.

Tip: The GC.GetTotalMemory method is used to determine the resource usage of the program before and after the allocation occurs.


Result: The nullable type wrapper requires 4 bytes of storage. And the integer itself requires 4 bytes for each element.

Opinion: This is an efficient implementation. In an array many nullable types are stored in contiguous memory.

C# program that computes memory usage for nullables using System; class Program { static void Main() { // // Compute the memory usage for a nullable type integer. // ... The program allocates one million nullable int structs. // const int size = 1000000; long b1 = GC.GetTotalMemory(true); int?[] array1 = new int?[size]; long b2 = GC.GetTotalMemory(true); array1[0] = null; Console.WriteLine((b2 - b1) / (double)size); } } Output 8.000016
Notes, nullables. With "null," we can indicate an int is invalid, missing or uninitialized. No special values (like -1) are needed. This can make code more reliable.
Implementation. When you use a type such as int?, the C# compiler actually uses the Nullable<T> struct, where T is the value type you are using such as int.Struct
Notes, structs. Structs are allocated in continuous memory, and this makes nullable types fairly efficient. There is overhead to using nullable types—raw values are slightly faster.
A summary. Nullable types are value types that are wrapped inside the nullable type. They can be useful when you want to add another state (invalid or uninitialized) to a value type.
Dot Net Perls
© 2007-2020 Sam Allen. Every person is special and unique. Send bug reports to