Introduced in C# 2.0, the null coalescing operator (??) allows one to program in a concise, declarative fashion when performing null checks. This was important in 2005 as it made it easier to utilize another important feature: nullable types.
Nullable types solved the problem of representing a value type that also contained a null value. This representation of value types is common in databases, and oftentimes caused problems when doing relational to object mapping.
Nullable types are generic structs that can be declared as Nullable<T>. However, they are often used with the value type and a question mark, e.g. int?. Here’s an example of how to use a nullable type and the null coalescing operator.
int number = nullable ?? default(int);
The null coalescing operator isn’t necessary, however. As I mentioned above, it makes your code more concise, and declarative. There are two other ways to do this.
The imperative way:
int number = default(int);
if (nullable.HasValue)
{
number = nullable.Value;
}
The longer, declarative way:
int number = nullable.HasValue ? nullable.Value : default(int);
The latter example is too long. The null coalescing operator achieves the same result with much less code, so it is preferable.
The imperative example will elicit some calls for support. I’ve heard the argument from several people before: “developers don’t understand this ?? thing, everyone understands if statements.” If that’s true in your organization, I would recommend teaching it to them. Start using the operator so they get used to it. If you keep providing them imperative crutches, they will continue to use them. No one learned standard object oriented programming overnight either. To continue pushing imperative code over reasonable declarative alternatives is akin to writing 150 line methods because that’s the way you did it in C.
So why am I writing about the null coalescing operator now, so many years after it came into being? The answer is that it can be combined with newer language features to make your code even more powerful.
The null coalescing operator with object initializers
In this example, I broke out some old school ADO.NET. The AdventureWorks database has a WorkOrder table with an EndDate column that allows nulls.
public static WorkOrder GetWorkOrderById(int id)
{
using (var connection =
new SqlConnection
(ConfigurationManager.ConnectionStrings["AdventureWorks"].ConnectionString))
{
connection.Open();
var command = new SqlCommand(Queries.GetWorkOrderById, connection);
command.Parameters.Add(new SqlParameter("@WorkOrderId", id));
var reader = command.ExecuteReader();
if (!reader.HasRows)
{
throw new ApplicationException(Exceptions.WorkOrderNotFound);
}
reader.Read();
return new WorkOrder
{
Id = reader.GetInt32(0),
Quantity = reader.GetInt32(1),
Stock = reader.GetInt32(2),
Scrapped = reader.GetInt16(3),
StartDate = reader.GetDateTime(4),
EndDate = reader[5] as DateTime? ?? DateTime.MaxValue,
DueDate = reader.GetDateTime(6),
ModifiedDate = reader.GetDateTime(7)
};
}
}
Unless the underlying schema changes, we will have a value for most of the columns. EndDate allows nulls in the database, but the WorkOrder object is straight DateTime in my sample application. The WorkOrder object has its values set in an object intializer, which requires a declarative syntax for all property initializations. The solve this in the simplest way possible, I used the null coalescing operator to make the end date the maximum possible value if the database has a null value.
The null coalescing operator with LINQ statements
LINQ is declarative by design, which makes it a good partner with the null coalescing operator where appropriate. Although you may never use it with a predicate (unless it’s a bool?), it is possible to use it with a selector.
int?[] dirtyNumbers = new int?[] { 1, null, 2, null, 3, 4, null, 5 };
var numbers = dirtyNumbers.Select(n => n ?? default(int));
You can extend this further make null have a meaning, such this function where null becomes the adjacent number in the aggregate function.
var values = new double?[] { 1.01, null, 1.24, 1.53, null, 1.93, 1.12 };
var value = values.Aggregate((x, y) => (x ?? y) * (y ?? x));
A more common scenario is that you want to retrieve an element out of enumerable. This is typically the first element, or perhaps it’s a single element. In any case, if it’s not found, you must retrieve it from elsewhere.
var workOrder = workOrders.SingleOrDefault(w => w.Id == id) ?? GetWorkOrderById(id);
I still see so many pieces of code where someone uses FirstOrDefault, or SingleOrDefault but leaves off the null coalescing operator. Instead, the programmer turns to imperative logic to handle the variable being null. It is important to point out that the null coalescing operator is useful for more than nullable types; it is useful for all reference types.