Introduction
Would you want to write safe, clean and maintainable code while accessing objects composed within other objects? The safe navigation operator can come to your rescue. But, it's not in C# yet. In this article, I shall share with you my way of emulating it.
Background
The safe navigation operator is available in a few other languages, for example the Groovy language:
String lname = person.name?.ToLowerCase();
The operator ?.
allows you to safely call the method ToLowerCase
on the member name
of the person
object. 'Safely' means without throwing a NullReferenceException
even when the name
member is null
. It will allow you to safely chain method calls when accessing a deep hierarchy of objects.
A closer look at the problem
Consider the following somewhat contrived example (in C#):
class A
{
public B b;
}
class B
{
public C c;
}
class C
{
public string name;
}
and we have an object of class A
assigned to a variable a
:
var a = new A() { b = new B() }
The above expression creates an object of class A
with its b
member set to a newly instantiated object of class B
. However, the c
member of the object referenced by b
is set to the default value null
, because it's not explicitly assigned any value. Now let's say somewhere later in the program, we wish to refer to the name
member of the c
object 'inside' the b
object inside the a
object using this expression:
a.b.c.name
This will surely fail at run time with the NullReferenceException
because the member c
is null
. We need to resort to the following conditional expression using the ternary operator ?:
(a != null && a.b != null && a.b.c != null ? a.b.c.name : null)
This not only looks clunky and but is also error prone. It becomes worse when the class members involved are methods and not variables. I will give a pragmatic example. The other day I was using the XElement
class for processing an XML file. The relevant part of the file is shown here:
<family>
<person gender="female">
...
</person>
<person>
...
</person>
</family>
The relevant part of my C# code was:
if (person.Attribute("gender").Value == "male")
{
... }
The code did its job correctly with the first person element in the XML shown above, but crashes with a NullReferenceException
at runtime when it tries to process the second element. Why? There's no gender
attribute on the second element, and there's no check for it in the code. The safe way of coding would have been:
if (person.Attribute("gender") != null
&& person.Attribute("gender").Value == "male")
{
...
}
This is only an example to show the cumbersome approach one has to resort to writing safe code - one has to repeat the call to the Attribute
method. In this specific case, the .NET API provides a type-specific cast that makes it convenient to write the same code in less verbose way. But, I digress. Had the current version of C# been endowed with the safe navigation operator, the same code could be rewritten like this:
if (person.Attribute("gender")?.Value == "male")
{
...
}
Having extolled the virtues of the safe navigation operator, and the fact that it's missing in the current versions of C# (version 5 and below), is there a possibility of emulating it. Yes, we can! In fact, the approach I will use below even surpasses the convenience offered by the safe navigation operator.
The Solution
Define an extension method as shown below:
static class Utils
{
public static Tresult NotNull<Tin, Tresult>(this Tin a, Func<Tin, Tresult> whenNotNull)
{
return a != null ? whenNotNull(a) : default(Tresult);
}
}
As you can see, the extension method is housed inside a static
class (as required by the compiler) which I have called Utils
. The extension method, by definition is static
so that it can be called without needing to instantiate the Utils
class. Also, buried inside the definition just before the first angle bracket ('<') is the name of the method - 'NotNull
'. The name is not very descriptive, but I couldn't find a more concise and expressive one. It's this method that emulates the functionality of the safe navigation operator. The method is a generic one that takes a couple of type parameters, called Tin
and Tresult
. Tin
is the type of object on which the method will be called as denoted by the method parameter a
. Note that the this
keyword appearing before the declaration of a
in the parameter list is what makes this method an extension method. This is as per the rules of an defining extension method and has nothing to do with the topic under discussion. And now, the interesting part: the second parameter is a delegate of the .NET type System.Func<Tin, Tresult>
. This means I can pass any delegate that 'points' to a method takes a single parameter of type Tin
and returns an object of type Tresult
. It's the same return type as that of the NotNull
method.
The body of the NotNull
method itself is a one-liner. We check whether a
is not null
and if so return the same object returned by the method pointed by the delegate parameter. If that's not the case (that is, if a
is null
), we return the default value of type Tresult
. This default value is null
for reference (class) types and 'empty' value for value types. In a later section, I will show you how to modify the above solution to take into account a custom-made value to return in case of a
being null
. Alright, enough of explanation, we shall now see the NotNull
method in action. Seeing the demo will also help clarify this wordy explanation.
Let's apply the method to the XML reading problem discussed a few paragraphs before. We rewrite the code:
if (person.Attribute("gender").NotNull(x => x.Value) == "male")
{
...
}
If you look closely at the code above, you will see that the NotNull
method is being called on the object of type Xattribute
(the return type of the Attribute
method). Comparing with the definition of the NotNull
method, we see that this is the Tin
parameter. The Tin
type parameter is XAttribute
in this specific case. The parameter a
of the NotNull
method is of type Xattribute
. Next, I have used a lambda expression to pass an anonymous method to the delegate parameter of the NotNull
method. All the anonymous method does is return the Value
member of the XAttribute
object. Can you tell what's the return type of the NotNull
method? That's the same return type as that of the anonymous method which is System.String
. You can double-check this by the type of the Value
member being returned by the anonymous method above. They are the same. As you can see, the NotNull
method doubles up as a safety check against null
reference. If the XAttribute
object returned by the Attribute
method is null
, the delegate is never invoked. And, in that case the value returned by the NotNull
method will be the default value of XAttribute
type, which is null
. It's this null
that's checked for equality with the string
"male
". Now, doesn't that code look elegant. There's no repetition of the Attribute
method call; it's readable and safe.
We shall now go back to the contrived example discussed at the beginning of this article. The example consisted of a set of three classes, A
, B
and C
. We were trying to access the name
member using an object of class A
. Here's the equivalent solution using the NotNull
method:
a.NotNull(x => x.b).NotNull(x => x.c).NotNull(x => x.name)
In the expression above, the first invocation of NotNull
, the type of object returned is B
; the second invocation results in object of type C
and the last invocation results in object of type System.String
(the type of member name
). What do you think, isn't this technique nifty?
Surpassing the power of the safe navigation operator
I mentioned that the approach I will be showing you will even surpass the convenience of the safe navigation operator. Now, I will make good that claim. Specifically, I will be targeting the 'short-coming' of the NotNull
method in that it's only able to return a default instance of type 'Tresult
'. To address this, let me introduce another overload of the NotNull
method:
public static Tresult NotNull<Tin, Tresult>
(this Tin a, Func<Tin, Tresult> whenNotNull, Func<Tresult> whenNull)
{
return a != null ? whenNotNull(a) : whenNull();
}
This overload of NotNull
method takes 3 parameters. The first two parameters are identical to that in the first overload, while the third parameter is the one that gives special powers to the method making it surpass the safe navigation operator in terms of the convenience offered. This third parameter is also a delegate, but of the standard .NET type System.Func<Tresult>
that 'points' to a method that takes no parameter and returns an object of type Tresult
. The only other part that's changed from the first overload is the body of the method. We are now returning the object returned from the method pointed by the second delegate parameter. To distinguish this parameter from the first delegate parameter, I have tried to come up with descriptive names for both. Finally, I settled on calling the first delegate parameter by the name whenNotNull
and the second delegate as whenNull
. Depending on whether the value of a
parameter is null
, I return the result of invoking one or the other delegate. Again, let's see this overload of NotNull
method in action.
The complete Utils
class, including the second overload of NotNull
method, now looks like this:
static class Utils
{
public static Tresult NotNull<Tin, Tresult>
(this Tin a, Func<Tin, Tresult> whenNotNull) {
return a != null ? whenNotNull(a) : default(Tresult);
}
public static Tresult NotNull<Tin, Tresult>(this Tin a, Func<Tin,
Tresult> whenNotNull, Func<Tresult> whenNull) {
return a != null ? whenNotNull(a) : whenNull();
}
}
In the XML processing code example, what if I wanted to express the fact that a missing gender
attribute on a person
element implies that the person is a male
. To clarify things, if I have the following XML:
<person gender="female">
...
</person>
<person>
...
</person>
implies that the second person element has the attribute gender
set to "male
". To express this succintly using our NotNull
methods, I would rewrite the C# code as below:
if (person.Attribute("gender").NotNull
(x => x.Value, () => "male") == "male")
{
...
}
Now, isn't that code looking elegant and expressive? The code invokes the second overload of the NotNull
method - the one that takes 3 parameters. Particularly, I pass a delegate to the whenNull
parameter that points to an anonymous method. The anonymous method takes no parameters and all it does is return the string
"male
". This delegate gets invoked when processing the second person
element in the XML file. Since the element has no gender
attribute, the C# expression person.Attribute("gender")
is null. When the NotNull
method is invoked, its a
parameter is null
and hence the return value is the result of invoking the whenNull
delegate. Hence, the result of the whole expression person.Attribute("gender").NotNull(x => x.Value, () => "male")
is the string "male"
Going back to the contrived example of the classes A
, B
and C
discussed at the beginning of this article, let me introduce an 'artificial' requirement. If the value of the c
member of an object of class B
is null
, we would like to use the following instance of C
:
readonly C defC = new C { name = "Krishna" };
This means given the following C# statement,
var a = new A { b = new B() };
the expression a.b.c.name
must return the string
"Krishna
".
Obviously, as seen earlier, the above expression will fail at runtime with a NullReferenceException
. We can use our NotNull
methods to address the problem in an elegant way:
a.NotNull(x => x.b).NotNull(x => x.c).NotNull(x => x, () => defC).name
In the above expression, the third call to NotNull
uses the second overload, while the first two calls use the first overload. In this specific case, the return value from the second call to NotNull
is null
which is passed to the third call to NotNull
. Since the a
parameter of the NotNull
method is null
, the delegate reference by the whenNull
parameter is invoked. In the above expression, we pass the lambda expression () => defC
to this delegate which simply returns the object referenced by the defC
variable. Finally, the name
member of this object is referenced. Thus the string
"Krishna
" is the result of this expression. What if the c
member was not null
? In that case, the third call to NotNull
uses the first lamda expression: x => x
, which simply returns the object referenced by c
. The result of the whole expression would then be whatever value the name
member of the object referenced by c
had at that time.
Incidentally, the expression accessing the name
member as shown above would return "Krishna
" even in cases where any of the objects in the method chain were null
! For example, if a
were defined like this:
A a = null;
Then the expression:
a.NotNull(x => x.b).NotNull(x => x.c).NotNull(x => x, () => defC).name
would still return "Krishna
"!. Why? The first call to NotNull
would return null
as that's the default value for objects of type A
. Due to this, the second call to NotNull
would also return null
. The third call would therefore invoke its second delegate parameter, which would return "Krishna
". Thus, a null
somewhere down the chain has a propagation effect. This may be the reason why some refer to the safe navigation operator as Null Propagation Operator.
Conclusion
In this article, we saw the problem of using the '.' operator in a chained fashion could lead to the dreaded NullReferenceException
. I presented the safe navigation operator. There's no such operator in C#. But, there's one planned in the upcoming release. Are we condemned to writing 'ugly' code using the ternary operator in a conditional expression or using some similar construct if we need to ensure exception safety? This article presented an elegant way of using generic extension methods that provided an elegant solution to this problem. We also saw how these methods can even surpass the power of the safe navigation operator when it does make its way into C#.
Enjoy programming!
Using the Code
The accompanying source code is a Visual Studio 2010 project. The project contains a single C# file, which you could compile and run from the command line too.
Points of Interest
The power of generics and lambda expressions in bestowing us, the programmers, in expressing our intent is noteworthy.
History
This is the first version of this article. If some of you, my readers, do not understand the examples using XElement
or XAttribute
the .NET XML API, I can give helpful pointers. This article is dedicated to my dear spiritual master His Divine Grace A. C. Bhaktivedanta Swami Prabhupada.