Want to learn about other C# 6 features? Check out the full list of articles & source code on GitHub
NULLs, you either hate them or you loath them. They are the source of so many issues in the apps we write, especially the dreaded NullReferenceException. The current fix is to be very defensive in your programming, for example:
public static void Exception(Track track) { // this may cause an exception if the track object, band property or the frontman property is null Console.WriteLine("HI! " + track.Band.FrontMan.Name); // defensive programming to avoid null exceptions if (track != null && track.Band != null && track.Band.FrontMan != null) { Console.WriteLine("HI! " + track.Band.FrontMan.Name); } }
Constant null checking also makes working with events particularly ugly, because you need to check for nulls before you call an event:
internal class NullConditionalEvent : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; private void RaisePain(string propertyName) { if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); } } }
NULLET
With C# 6, a new operator has been added; which is officially called the null propagation operator: ?.
Some people have taken to calling it the Elvis operator as it looks like two eyes & a splash of hair similar to Elvis, however I think that hair looks a lot more like a mullet so I have taken to calling this the NULLET operator.
The nullet operator allows us to check for nulls, so we can change the first example to the following which will NOT raise a a null exception:
public static void Exception(Track track) { Console.WriteLine("HI! " + track?.Band?.FrontMan?.Name); }
The way the nullet works, is that it tells .NET to check if the preceding item is null BEFORE moving to the next item. If the item is null, then it stops evaluation at that point and returns null, for example with track:
If you want the internals of this logic, check out this amazing post on Roslyn Codeplex site.
The important question is what will be the outcome of our above example; if any of the properties are null then it will be “HI!” + NULL which becomes “HI! “ and so that is what will be written to the screen.
Events & Methods
In the first example, there is an example of events and how you need to check for null before raising it. A side note is I am using events as the example, but this applies to any delegate. In the following example you can see how the nullet can help us clean up the event code:
private void RaisePain(string propertyName) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); }
To be able to call the event we need something for the nullet to do after the evaluation, so we use the invoke method. This is also a great example of how the nullet is more than just support properties, it works with methods too.
Null + Indexers = Support for arrays & lists
Nullet can work indexers as well, with a small syntax change – instead of ?., you have just ? (i.e. just a question mark, no trailing full stop). In both scenarios below the code happily executes with NO null issues because we have added the ? between the object and the indexer.
Track[] album1 = null; Console.WriteLine("Album 1 Track 6 " + album1?[5]); List<Track> album2 = null; Console.WriteLine("Album 2 Track 3 " + album2?[4]);
Not the end of all your null pain
The key fact around nullet is that it is a short hand in your code which prevents NullReferenceException but you can still have exceptions related to NULL. An example of this when you use Enum.Parse:
Enum.Parse(typeof(Example), track?.Band?.FrontMan?.PrimaryInstrument);
In this scenario, if you have any section as null, it will return a null, however the Enum.Parse method cannot handle null and will raise a NullArgumentException. In this case the method you are passing the null too is key, so you still need to make sure you are checking for nulls in some scenarios, but you can use nullet to make the checking much cleaner:
if (!string.IsNullOrWhiteSpace(track?.Band?.FrontMan?.PrimaryInstrument)) { Enum.Parse(typeof(Example), track?.Band?.FrontMan?.PrimaryInstrument); }