3 misuses of ?. operator in C# 6

By Vladimir Khorikov

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.





  • Sibbl

    First of all, great article! I caught myself thinking about my coding style just because of the new features that C# came with a lot as well. This is one part of it…

    Regarding #3… I understand your point while I also think that explaining the .? operator using such examples is not bad. Even if you introduce a separate method, what do you think the code inside this method might look like? I don’t think this is a misuse but nothing more than a reminder to think before writing down code.

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      Thank you!

      Regarding #3 – I totally agree with you. The code inside the GetAdminAddress() method can use the ?. operator, and I personally wouldn’t consider it a bad practice, because in this case it doesn’t break encapsulation. I just tried to emphasize that in some cases, the use of the ?. operator can hide bad design decisions.

  • jakkaj10

    Good stuff. I love seeing people start to set out new guidance around new bits.

    The main usage case we’ve found so far is working with entities in an unknown state (like a big nasty from a server).

    Perhaps a rule might be – it’s okay for navigating properties and things that don’t return or alter state (like event raising)

    This is nasty -?.?.Property().?… What kind of program flow do you think you have doing something like that (plus it’s clearly not an entity with all those methods!). That said – Perhaps there is one exception- those entities have extension methods… Like x?.Person?.Save() or something (some system level tight operation on the object that only alters state on that object)…

    Ps that entity Nav stuff – it’s great for jamming bits in to ViewModels!

  • nelsonlaquet

    For the event example, you do not need to alias the delegate into a local variable when using the ?. operator. I’ve yet to install the new compiler on my machine, so I can’t verify the decompiled code, but I’ve read multiple times on official MSDN articles that the delegate reference will indeed be copied out into a local variable behind the scenes. So your code would simply be:

    NameChanged?.Invoke(name);

    • http://enterprisecraftsmanship.com/ Vladimir Khorikov

      You are right. Updated the code sample, thank you!

  • michael lang

    With great power comes great responsibility. There’s thousands of ways to misuse many of the constructs in C#. This doesn’t mean these constructs shouldn’t be there.

    This is why every decent software house has coding standards, which should guide developers on the proper use of the constructs within a programming language. This article highlights some of the things owners of coding standards will need to think about for C# 6..

  • Ferdinando Santacroce

    Good work, thanks for sharing 🙂