Abstract
This article will help you to understand the Nullable type implementation in C#.
This article also explains about Coalescing Operator and how CLR has special support for Nullable value type.
Introduction
As we all knows that a value type variable cannot be null
. That's why they are called Value Type.
Value type has a lot of advantages, however, there are some scenarios where we require value type to
hold null also. For instance, If you are retrieving nullable integer column data from database table,
and the value in database is null, there is no way you can assign this value to an C# int. Let's have
an another scenario: In Java, java.Util.Date
is a reference type, and therefore, the variable of this
type can be set to null. However, in CLR, System.DateTime
is a value type and a DateTime
variable cannot
be null. If an application written in Java wants to communicate a date/time to a Web service running on
the CLR, there is a problem if the Java application sends null because the CLR has no way to represent
this and operate on it.
To get rid of these situations, Microsoft added the concept of Nullable
types to the CLR.
To Understand this, have a look over the definition of System.Nullable
<t> Type:
[Serializable, StructLayout(LayoutKind.Sequential)]
public struct Nullable<t> where T : struct
{
private Boolean hasValue = false;
internal T value = default(T);
public Nullable(T value)
{
this.value = value;
this.hasValue = true;
}
public Boolean HasValue { get { return hasValue; } }
public T Value
{
get
{
if (!hasValue)
{
throw new InvalidOperationException(
"Nullable object must have a value.");
}
return value;
}
}
public T GetValueOrDefault() { return value; }
public T GetValueOrDefault(T defaultValue)
{
if (!HasValue) return defaultValue;
return value;
}
public override Boolean Equals(Object other)
{
if (!HasValue) return (other == null);
if (other == null) return false;
return value.Equals(other);
}
public override int GetHashCode()
{
if (!HasValue) return 0;
return value.GetHashCode();
}
public override string ToString()
{
if (!HasValue) return "";
return value.ToString();
}
public static implicit operator Nullable<t>(T value)
{
return new Nullable<t>(value);
}
public static explicit operator T(Nullable<t> value)
{
return value.Value;
}
}
</t></t></t></t>
From the above definition, you can easily make out that:
- Nullable<t> type is also a value type.
- Nullable Type are of struct type that holds a value type (struct) and a
Boolean
flag, named HasValue
, to indicate whether the value is null
or not. - Since Nullable<t> itself is a value type, it is fairly lightweight. The size of
Nullable<T>
type instance is same as the size of containing value type plus the size of a boolean
. - The nullable types parameter
T
is struct
. i.e., you can use nullable type only with value types. This is quite ok because reference types can already be null
. You can also use the Nullable<T>
type for your user defined strut
. - Nullable type is not an extension in all the value types. It is a struct which contains a generic value type and a
boolean
flag.
Syntex and Usage
To use Nullable type, just declare Nullable struct
with a value type parameter, T
,
and declare it as you are doing for other value types.
For example,
Nullable<int> i = 1;
Nullable<int> j = null;
Use Value
property of Nullable type to get the value of the type it holds. As the definition says,
It will return the value if it is not null
, else, will throw exception. So, you may need to check for
the value being null
before using it.
Console.WriteLine("i: HasValue={0}, Value={1}", i.HasValue, i.Value);
Console.WriteLine("j: HasValue={0}, Value={1}", j.HasValue, j.GetValueOrDefault());
i: HasValue=True, Value=5
j: HasValue=False, Value=0
Conversions and Operators for Nullable Types
C# also supports simple syntax to use Nullable types. It also supports implicit conversion and casts on
Nullable instances. The following example shows this:
int? i = 5;
int? j = null;
int k = (int)i;
Double? x = 5;
Double? y = j;
C# allows you to use operators on Nullable types as you can use it for the containing types.
- Unary operators (++, --, -, etc) returns
null
if the Nullable types value is set to null
. - Binary Operator (+, -, *, /, %, ^, etc) returns
null
if any of the operand is null
. - For Equality Operator, if both operands are
null
, expression is evaluated to true
. If either operand is null
, it is evaluated to false
. If both are not null
, It compares as usual. - For Relational Operator (>, <, >=, <=), if either operand is
null
result is false
and if none of the operand is null
, compares the value.
See the example below:
int? i = 5;
int? j = null;
i++;
j = -j;
i = i + 3;
j = j * 3;
if (i == null) { } else { }
if (j == null) { } else { }
if (i != j) { } else { }
if (i > j) { } else { }
The Coalescing Operator
C# provides you a quite simplified syntax to check null
and simultaneously assign another value
in case the value of the variable is null
. This can be used in Nullable types as well as reference types also.
For example, The code below:
int? i = null;
int j;
if (i.HasValue)
j = i.Value;
else
j = 0;
j = i ?? 0;
string pageTitle = suppliedTitle ?? "Default Title";
string fileName = GetFileName() ?? string.Empty;
string connectionString = GetConnectionString() ?? defaultConnectionString;
int age = employee.Age ?? 0;
int?[] numbers = { };
int total = numbers.Sum() ?? 0;
Customer customer = db.Customers.Find(customerId) ?? new Customer();
string username = Session["Username"] ?? string.Empty;
Employee employee = GetFromCache(employeeId) ?? GetFromDatabase(employeeId);
You can also chain it, which may save a lot of coding for you. See the example below,
string address = string.Empty;
string permanent = GetPermanentAddress();
if (permanent != null)
address = permanent;
else
{
string local = GetLocalAddress();
if (local != null)
address = local;
else
{
string office = GetOfficeAddress();
if (office != null)
address = office;
}
}
string address = GetPermanentAddress() ?? GetLocalAddress() ?? GetOfficeAddress() ?? string.Empty;
The code above with Coalescing operator is far easier to read and understand than that of a nested if else chain.
Boxing and UnBoxing of Nullable types
Since I have mentioned earlier that the Nullable<T>
is still a value type,
you must understand performance while boxing and unboxing of Nullable<T>
type.
The CLR executes a special rule to box and unbox the Nullable types.
When CLR is boxing a Nullable instance, it checks to see if the value is assigned null
.
In this case, CLR does not do anything and simply assigns null
to the object
. If the
instance is not null
, CLR takes the value and boxes it similar to the usual value type.
While unboxing to Nullable type, CLR checks If an object
having its value assigned to null
.
If yes, it simply assigns the value of Nullable type to null
. Else, it is unboxing as usual.
int? n = null;
Object o = n;
Console.WriteLine("o is null={0}", o == null);
n = 5;
o = n;
Console.WriteLine("o's type={0}", o.GetType());
Object o = 5;
int? a = (Int32?) o;
int b = (Int32) o;
o = null;
a = (int?) o;
b = (int) o;
Calling GetType() for Nullable Type
When calling GetType()
for Nullable<T>
type, CLR actually lies and returns the Type the Nullable type it holds.
Because of this, you may not be able to distinguish a boxed Nullable<int>
was actually a int or Nullable<int>
.
See the example below:
int? i = 10;
Console.WriteLine(i.GetType());
Points of Interest
Note that I haven't discuss about detail of the memory allocation and object creation while boxing and
unboxing to keep the article focused to Nullable
types only. You may google it for details about boxing and unboxing.
Conclusion
Since Nullable
Type is also a value type and fairly lightweight, don't hesitate to use it. It is quite useful in your data driven application.
History
- Version 1.0: Nov 01, 2011
Anurag Gandhi is a Freelance Developer and Consultant, Architect, Blogger, Speaker, and Ex Microsoft Employee. He is passionate about programming.
He is extensively involved in Asp.Net Core, MVC/Web API, Node/Express, Microsoft Azure/Cloud, web application hosting/architecture, Angular, AngularJs, design, and development. His languages of choice are C#, Node/Express, JavaScript, Asp .NET MVC, Asp, C, C++. He is familiar with many other programming languages as well. He mostly works with MS SQL Server as the preferred database and has worked with Redis, MySQL, Oracle, MS Access, etc. also.
He is active in programming communities and loves to share the knowledge with others whenever he gets the time for it.
He is also a passionate chess player.
Linked in Profile: https://in.linkedin.com/in/anuraggandhi
He can be contacted at soft.gandhi@gmail.com