Home
Map
Nullable ExamplesExamine nullable types, including nullable ints and DateTimes. Test the memory usage of nullables.
C#
This page was last reviewed on Apr 11, 2023.
Nullable. With nullable types in C# programs, every value type, like int, can be null. We wrap an int inside a nullable struct to have a null int.
null
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 variable is specified with a "?" character. 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.
Property
Info The int type is aliased to System.Int32—using "System.Int32?" would work as well.
System
Detail The HasValue property returns a bool that indicates whether the nullable instance contains a set value.
return
Next If the HasValue property is true, you can access the Value property without an exception.
if
using System; // // 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"); }
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.
bool
Info This syntax enables us to represent a null, true and false value in a single variable.
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); } }
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.
enum
And Unlike the nullable bool, all 3 values can be represented in one byte of storage.
byte, sbyte
Tip The enum type avoids the overhead associated with wrapping a value type in a generic struct.
Generic
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); } }
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.
Here The "DateTime?" variable is declared. It is passed as a parameter to the Test method, and is assigned to different values.
Note The GetValueOrDefault method will return DateTime.MinValue for a null DateTime.
DateTime.MinValue
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); } } }
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 syntax has the same effect, but is more verbose.
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"); } } }
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.
GC.Collect
Result The nullable type wrapper requires 4 bytes of storage. And the integer itself requires 4 bytes for each element.
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); } }
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 nullable type, the C# compiler actually uses the Nullable T struct. The T refers to the value type you are using (such as int).
Note Structs are allocated in continuous memory, and this makes nullable types fairly efficient.
struct
Note 2 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 is a collection of tested code examples. Pages are continually updated to stay current, with code correctness a top priority.
Sam Allen is passionate about computer languages. In the past, his work has been recommended by Apple and Microsoft and he has studied computers at a selective university in the United States.
This page was last updated on Apr 11, 2023 (simplify).
Home
Changes
© 2007-2024 Sam Allen.