Previously, I wrote about String Interpolation and Expression-bodied members, new features to C# 6.0. Today I will be looking at the Null-Conditional operator, which is also new to C# 6.0.
Null
This will be a bit of a tangent, but I thought I’d cover null itself. Most of us, I expect, have an understanding of what “Null” is (or rather, what it isn’t). Nonetheless, The idea of null itself has some controversy surrounding it, in a sense. Many programming languages use a concept of null to represent Nothing; Visual Basic’s equivalent to Null is in fact called Nothing. There is a “movement” of sorts which effectively claims that programming would be less trouble-prone if we got rid of Null; if Null was non-existent. Generally, the idea is that a lot of programming exceptions are errors such as Null Reference Exceptions and null pointers; therefore, the logic is that by removing the possibility for null, we remove the possibility for those errors and therefore we eliminate the problem.
I think that eliminating nulls to simplify programming is like trying to eliminate 0 to simplify math; we created the concept because it made it easier to demonstrate and express abstract thinking. In eliminating Nulls, we simply end up with other bugs. The only real alternative would be to force no instance to ever be null by making a “Empty” instance be instead a blank instance. But that just raises further issues, both in terms of how that default get’s set (What is a default state for a Socket?) as well as exactly what problems that would solve- instead of trying to access a method throwing a NullReferenceException erroneously, that instance will be a default instance and the method call will not cause that error but may cause other errors within the method which may simply be even harder to trace than the NullReferenceException. It is trading one set of errors for another set of errors entirely, and furthermore that new set of errors will be even more difficult to diagnose.
Now that I’ve got that out of my system, we still don’t want to deal with NullReferenceExceptions if we can avoid it. Thankfully, it is fairly easy to program defensively if you suspect an instance variable might be null:
1 2 3 4 5 6 7 8 9 10 11 12 |
public void RunPlugin(Plugin p) { if(p==null) throw new NullArgumentException("p"); ICorePlugin cp = p As ICorePlugin; if(cp!=null){ CorePlugins[cp] = p.Name; cp.Initialize(this); } p.Load(this); LoadedPlugins.Add(p); } |
In this instance we have a Plugin abstract class definition but it is also possible that a Plugin implements an extended interface ICorePlugin. In this case if we find it is a core plugin we need to take extra steps- for obvious reasons we cannot simply index into the dictionary or call a method on the instance if it doesn’t implement the type, as if it doesn’t implement the interface the As case will return null. With the Null-Conditional operator, we can replace the null checks with- well, the operator:
1 2 3 4 5 6 7 8 9 10 |
public void RunPlugin(Plugin p) { if(p==null) throw new NullArgumentException("p"); ICorePlugin cp = p As ICorePlugin; String initname = CorePlugins?[cp]; cp?.Initialize(this); p.Load(this); LoadedPlugins.Add(p); } |
As we can see here, after casting the type to a ICorePlugin, we merely access the possibly-null value via the conditional operator, as well as call functions and indexers which accept that parameter differently, via the conditional. The Conditional operator using a question mark makes semantic sense given that the null coalescing operator is ??.
improve discussion
Thank you very much
The ?. usage has become known to some as the “Elvis Operator”. The stroke of the question mark, I’m told, looks like his hair. Presumably this naming scheme takes after the “Spaceship” operator “< =>” in that it is named after what it looks like rather than what it does. Not that that is a bad thing but I’m of the mind that it rather makes sense to name and call operators by names that describe what they do, however once you get passed this sort of jargon such a name reasonably becomes far less amusing, so funny names I suppose keep interest.
One interesting consideration is that it is sort of the ? that is the operator on it’s own, and you can simply use it in a few instances for implicit null-checks. ?. allows you to have an implicit null check when accessing an instance member, and you can do something similar for index access operators (as shown) as well.
The flip-side
Adding a lot of operators with interesting capabilities definitely expands the language but there is an argument to be made about language complexity. Furthermore, while it can be argued that if we do not like a new feature, we can simply not use it, that realistically only applies to code we write. If we are working in a team, than decisions need to be made about how things are done and these sorts of operators might be considered “off-limits” simply to reduce the complexity of the codebase. This is hardly an argument against adding these features but more complex languages tend to become less accessible to newer developers and adding complex syntax and parsing rules can result in confusion. Thankfully these operators are effectively syntax sugar and can be learned after learning the “long way” involving proper null-checking, before these operators are introduced.
Have something to say about this post? Comment!