Introduction
This article talks about Nullable
types in C#. We will see when could we find ourselves in need for Nullable
types and what should we know if we are dealing with Nullable
types.
Background
The null
value is very useful in C#. For any type the null
could be used to identify whether this particular
variable contains a value or not. Typical use of null
is to assign a variable null
value to indicate that this variable
has not been initialized. Whenever we need use this, we first check whether it is null
or not, if it is null then perhaps
we need to initialize and use it and if it is not then we can use it right away.
Now if we consider the same scenario for built in numeric types or struct types, then How will we represent the absence of value. In an int type all the values are valid int
values. The moment I declare int,
it contains
some default value. So is it possible to use null
assignment and checking with int
(or struct types)?
The answer to the above question is - No, it is not possible to assign/check with null
value for numeric types and
struct types. So if we still need to have the possibility of checking for null
for numeric types and structs, we need
to define them as Nullable
types.
Using the code
So let us start by looking at how we can use null
with reference types. Let us have a simple class and null
will
represent the absence of value.
class A
{
public void Display()
{
Console.WriteLine("Displaying from Class A.");
}
}
static void Main(string[] args)
{
A a = null;
if (a == null)
{
a = new A();
a.Display();
a = null;
}
}
How to create and use Nullable types
If this same entity would have need defined as an struct then it would not be possible to use null
with it.
struct B
{
public void Display()
{
Console.WriteLine("Displaying from Struct B.");
}
}
static void Main(string[] args)
{
}
So if we still need the object of class B
to have the possibility of having null representing a
null value then we need to create a Nullable
type for B. A Nullable
type is used to represent a
possible missing value of a type.
A Nullable
type can be created as Nullable<T>
. The T
here is the type that cannot have null
values. The type Nullable<T>
will either have a null
value or any possible value that T
can have. So let us
define a Nullable
type for the above mentioned struct and see how we can use it.
static void Main(string[] args)
{
Nullable<B> b2 = null;
if (b2 == null)
{
b2 = new B();
b2.Value.Display();
b2 = null;
}
}
And if we need to create a Nullable
int then we can do it as
static void Main(string[] args)
{
Nullable$ltint> i = null;
if (i == null)
{
i = 5;
Console.WriteLine(i.ToString());
}
}
Shorthand for using Nullable types
Instead of writing Nullable<T>
every time, we can use the shorthand version for creating
The Nullable
types as T? t
. Following code snippet shows how to use the shorthand notation to
create the Nullable
types.
static void Main(string[] args)
{
int? i2 = null;
if (i2 == null)
{
i2 = 15;
Console.WriteLine(i2.ToString());
}
}
How is Nullable defined
The Nullable
type is defined as a struct and this itself is a value type. Following code snippet
shows the major properties and functions that we might be needing if we are using a Nullable
type.
public struct Nullable<T> where T : struct
{
public bool HasValue { get; }
public T Value { get; }
public T GetValueOrDefault();
public T GetValueOrDefault(T defaultValue);
public override string ToString();
}
The HasValue
property will return true
if the current Nullable<T>
object has a value
otherwise it will return false
.
The Value property will return the value of the Nullable<T>
object if the Nullable<T>.HasValue
is true. if HasValue
returns false then it will throw an exception.
The first version of GetValueOrDefault
will return the value of the contained T
type if it has some
value otherwise it will return the T
's default value. The second version will return the specified
default value(passed as parameter) if the Nullable<T>
has no value.
And finally the ToString
method will return the string representation of the value contained in
type T
.
Conversion between Nullable and non-Nullable types
Once we create a Nullable
type, we will find our self in need to convert it to and from the actual type.
To understand how this conversion works, we need to look into how the conversion operators are defined in
the Nullable
type.
public struct Nullable<T> where T : struct
{
public static explicit operator T(T? value);
public static implicit operator T?(T value);
}
So looking at the above function declarations, it is clear that Conversion from T
to Nullable
is implicit but Conversion from Nullable
to T
is explicit.
static void Main(string[] args)
{
int? i3 = null;
int i4 = 50;
i3 = i4;
i4 = (int)i3;
}
Boxing and Unboxing Nullable types
If we perform boxing on a T?
then the the T
will be boxed and not the T?
. This optimization is provided by the
C# itself. Also we can unbox any value and get the Nullable
type in return. This is useful when we need to check for
null
after unboxing. Following code snippet shows how the boxing and unboxing works in unison with Nullable
types.
static void Main(string[] args)
{
DateTime? dt = null;
dt = DateTime.Now;
object o = dt;
DateTime? dt2 = o as DateTime?;
if (dt2 != null)
{
Console.WriteLine(dt2.Value.ToString());
}
}
Null Coalescing Operator
We have already discussed the GetValueOrDefault
function above which will return the value of the
contained T type if it has some value otherwise it will return the default value. This same thing can
be achieved by using the Null coalescing operator ??
. This operator is a syntactic sugar to perform the same
operation as GetValueOrDefault(T defaultValue)
.
static void Main(string[] args)
{
int? j = null;
int? k = 54;
int result1 = j ?? 0;
int result2 = k ?? 0;
Console.WriteLine("result1 = {0}, result2 = {1}", result1, result2);
}
Operators and Nullable types
Before we finish let us look at the use of operators with Nullable
types i.e. Operator lifting.the concept of
operator lifting is that if we use Nullable
types with operators then it could use the operators on underlying
types but once it has checked for null
value. Though we can use on Nullable
types
as if we are using it for the containing types, there are some things to be kept in mind.
- Unary operators: Result will be
null
if the Nullable
types value is null
.
- Binary Operator: Result will be
null
if any of the operands is null
.
- Equality Operator: if both operands are
null
, result will be true
. If only one of the operand is null
, it is evaluated to false
. otherwise underlying types will be compared for equality.
- Relational Operator: if any operand is
null
, the result is false otherwise the comparison will be made on underlying type's values.
Point of Interest
In this small article, I have tried to talk about the Nullable
types in C#. We have seen the basic concepts and usage principles
of Nullable
types.
History
- 01 September 2012: First version.