C# Nullable ExamplesExamine nullable types, including nullable ints and DateTimes. Test the memory usage of nullables.
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.
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.
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"); } } }
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); } }
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); } }
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?".
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); } } }
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.
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"); } } }
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); } }
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).
Structs Structs are allocated in continuous memory, and this makes nullable types fairly efficient.
However 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.
© 2007-2021 sam allen. see site info on the changelog