The more I work with C# 6 in projects, the more I find myself using ?.
to write cleaner, simpler, and more readable code. Here are four different uses I’ve found for the null
coalescing operator.
Deep Containment Designs
Suppose I’m writing code that needs to find the street location for the home address for a contact person for vendor. Maybe there’s an awesome event, and I need to program my GPS. Using earlier versions of C#, I’d need to write a staircase of if
statements checking each property along the way:
var location = default(string);
if (vendor != null)
{
if (vendor.ContactPerson != null)
{
if (vendor.ContactPerson.HomeAddress != null)
{
location = vendor.ContactPerson.HomeAddress.LineOne;
}
}
}
Now, using C# 6, this same idiom becomes much more readable:
var location = vendor?.ContactPerson?.HomeAddress?.LineOne;
The null
coalescing operator short-circuits, so evaluation stops as soon as any single property evaluates as null
.
INotifyPropertyChanged and Similar APIs
We’ve all seen code like this in a class the implements INotifyPropertyChanged
:
public string Name {
get { return name; }
set {
if (name != value)
{
name = value;
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
private string name;
I hope you are cringing now. This code will crash if it’s used in a situation where no code subscribes to the INotifyPropertyChanged.PropertyChanged
event. It raises that event even when there are no listeners.
When faced with that situation, many developers write something like the following:
public string Name {
get { return name; }
set {
if (name != value)
{
name = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("Name"));
}
}
}
private string name;
OK, this is a little better, and will likely work in most production situations. However, there is a possible race condition lurking in this code. If a subscriber removes a handler between the ‘if
’ check and the line that raises the event, this code can still crash. It’s the kind of insidious bug that may only show up months after deploying an application. The proper fix is to create a temporary reference to the existing handler, and raise the event on that object rather than allowing the race condition on the PropertyChanged public
event:
public string Name {
get { return name; }
set {
if (name != value)
{
name = value;
var handler = PropertyChanged;
if (handler != null)
handler(this, new PropertyChangedEventArgs("Name"));
}
}
}
private string name;
It’s more code, and it’s a few different techniques to remember every time you raise the PropertyChanged
event. In a large program, it seems like someone forgets at least once.
C# 6 to the Rescue!
In C# 6, the null
coalescing operator implements all the checks I mentioned above. You can replace the extra checks, and the local variable with a ?
. and a call to Invoke
:
public string Name {
get { return name; }
set {
if (name != value)
{
name = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Name"));
}
}
}
private string name;
The shorter, more modern version reads more concisely, and implements the proper idioms for raising events and managing subscribers being added or removed.
Resource Management
Occasionally, we may find that one of our types owns another object that has certain capabilities. However, that object may implement other capabilities beyond those our class requires. Usually, that’s not an issue, but what if that object implements IDisposable
? Consider the case of an Evil Genius that is done working with a henchman. The code to retire a henchman might look like this:
public void RetireHenchman()
{
var disposableMinion = Minion as IDisposable;
if (disposableMinion != null)
disposableMinion.Dispose();
Minion = null;
}
The null
coalescing operator can make this code more concise as well:
public void RetireHenchman()
{
(Minion as IDisposable)?.Dispose();
Minion = null;
}
LINQ Queries
There are two different uses I’ve found for this operator when I work with LINQ queries. One very common use if after I create a query that uses SingleOrDefault()
. I’ll likely want to access some property of the (possible) single object. That’s simple with ?
.
var created = members.SingleOrDefault(e => e.name == "dateCreated")?.content;
Another use is to create a null
output whenever the input sequence is null
:
members?.Select(m => (XElement)XmlValue.MakeValue(m))
The addition of this feature has made me realize just how much code checks for null
, and how much more concise and readable our code will be by having a more concise syntax for checking against null
, and taking some default action based on the ‘null
-ness’ of a variable.
This feature has changed how I code everyday. I can’t wait for the general release and getting more of my customers to adopt the new version.