3 misuses of ?. operator in C# 6

I guess you already know about the safe navigation operator (?. operator) coming up in C# 6. While it’s a nice syntactic sugar for quite a few cases, I’d like to point out some misuses of it I’m sure we will see when C# 6 is released.

Misuse #1

I remember I read on one of the forums someone said that C# team should make ?. operator the default behavior for navigation instead of the . one. Let’s talk about it a little bit. Why not use it as the default option, really? Or, in other words, why not use the ?. operator everywhere, just in case?

One of the main purposes of your code is to reveal your intention to other developers. Using the ?. operator as the default option leads to a situation where developers are unable to understand whether a method is designed to return null or someone just put this check there because it was easy to do.

public void DoSomething(Order order)
{
    string customerName = order?.Customer?.Name;
}

Does this method really expect the order parameter to be null? Is the Customer property of it also supposed to turn into null? The code conveys that yes, both the order parameter and the Customer property can be null. But it’s no longer the case if the author has put it there without consciousness.

Putting the ?. operator in code without an actual need for it leads to confusion and misunderstanding.

This strongly correlates with the lack of non-nullable reference types in C#. It would be much easier to follow the program flow if they are introduced. Also, such code would be simply illegal:

public void DoSomething(Order! order)
{
    Customer customer = order?.Customer; // Compiler error: order can't be null
}

Misuse #2

Another possible misuse of the ?. operator is relying on nulls too much. Look at this code sample:

List<string> list = null;
int count;
if (list != null)
{
    count = list.Count;
}
else
{
    count = 0;
}

It has an obvious design smell. Instead of using null, it is preferable to leverage the Null Object design pattern instead:

// An empty list is an example of the Null Object design pattern usage
List<string> list = new List<string>();
int count = list.Count;

Now, with the new ?. operator, the design smell isn’t so obvious anymore:

List<string> list = null;
int count = list?.Count ?? 0;

In most cases, Null Object pattern is much better choice than null. Not only does it allow for null checks elimination, but it also helps better express domain model throughout the code. The use of the ?. operator might assist with the null checks elimination, but it can never substitute the need for domain modeling.

It might seem extremely easy to write such code:

public void Process()
{
    int result = DoProcess(new Order(), null);
}

private int DoProcess(Order order, Processor processor)
{
    return processor?.Process(order) ?? 0;
}

Whereas, it would be more convenient to introduce a Null Object:

public void Process()
{
    var processor = new EmptyProcessor();
    int result = DoProcess(new Order(), processor);
}

private int DoProcess(Order order, Processor processor)
{
    return processor.Process(order);
}

Misuse #3

Often, code like the following is shown as an example of a good use for the new ?. operator:

public void DoSomething(Customer customer)
{
    string address = customer?.Employees
        ?.SingleOrDefault(x => x.IsAdmin)?.Address?.ToString();
    SendPackage(address);
}

While it’s true that such approach allows you to reduce the number of lines in the method, it implies that such sequence of method invocations itself is somewhat acceptable.

The code above breaks encapsulation principles. From a domain modeling perspective, it is much more convenient to introduce a separate method:

public void DoSomething(Customer customer)
{
    Contract.Requires(customer != null);

    string address = customer.GetAdminAddress();
    SendPackage(address);
}

Thus, keeping objects’ encapsulation in place and eliminating the need for the null checks altogether. The use of the ?. operator may hide problems with entities’ encapsulation. It is better to resist the temptation to use the ?. operator in such cases, even if it might seem extremely easy to chain properties and methods one after another.

?. operator in C# 6: use cases

So, what are the actual use cases for the new ?. operator? First of all, it is legacy code. If you work with code or a library to which you don’t have access (or just don’t want to touch it), you might not have any choice other than working with the model it introduces. In this case, you can leverage the new safe navigation operator in a way that helps you to reduce the amount of lines required to work with the legacy code base. Another good example would be raising an event:

protected void OnNameChanged(string name)
{
    NameChanged?.Invoke(name);
}

Other use cases boil down to the ones that don’t fall to the 3 categories described earlier.

Summary

While the safe navigation operator can help reduce the number of lines in some cases, it can also hide design smells which would be more apparent without it.

You can use an imaginary experiment to decide whether or not you should use the ?. operator. Just think if the code without it is acceptable. If it is, then use the operator. Otherwise, try to refactor the code and remove the design flaws, rather than hide them.

Subscribe


I don't post everything on my blog. Don't miss smaller tips and updates. Sign up to my mailing list below.

Comments


comments powered by Disqus