📜 ⬆️ ⬇️

3 ways to use the operator? .. wrong in C # 6

Surely you already know about the operator of safe navigation ("?." Operator) in C # 6. While this is a pretty good syntactic sugar, I would like to point out the abuse options of this operator.

Option 1


I remember how I read on one of the forums as someone was saying that the team developing C # should do the behavior with "?." (automatic check for null) by default behavior when navigating inside objects instead of what is now happening with ".". Let's talk a little about it. Indeed, why not change the implementation for "."? In other words, why not use the "?." operator everywhere, just in case?

One of the main purposes of your code is to disclose your intentions to other developers . Universal use of the operator "?." instead of "." leads to a situation in which developers are not able to understand whether the code really expects null or someone put this check simply 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? Can the Customer property also return null? This code reports that yes: both the order parameter and the Customer property can be null. But it ceases to be so if the author placed these checks just like that, without understanding what he is doing.
')
Using the operator "?." in code without a real need for it leads to confusion and degrades the readability of the code .

This problem is closely related to the absence of non-zero reference types in C # . It would be much easier to read the code if they were added at the language level. In addition, such code would lead to a compilation error:

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

Option 2


Another way to abuse the operator "?." - rely on null where it is not required. Consider an example:

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

This code has an obvious "smell". Instead of using null, in this case it is better to use the pattern Null object :

 //   -    Null object List<string> list = new List<string>(); int count = list.Count; 

Now with the new "?." operator, the "smell" is not so obvious:

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

In most cases, the Null object pattern is a much better choice than null. It not only allows you to eliminate checks for null, but also helps to better express the domain model in the code. Using the operator "?." can help with the elimination of checks for null, but can never replace the need to build a qualitative domain model in the code .

With the new operator it is very easy to write similar code:

 public void Process() { int result = DoProcess(new Order(), null); } private int DoProcess(Order order, Processor processor) { return processor?.Process(order) ?? 0; } 

While it would be better to express this logic using the 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); } 

Option 3


Often this code is shown as a good example of applying a new operator:

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

While such an approach does reduce the number of lines in a method, it implies that such code in itself is something acceptable.

The code above violates the encapsulation principles . From the point of view of the domain model, it would be much more correct to add a separate method:

 public void DoSomething(Customer customer) { Contract.Requires(customer != null); string address = customer.GetAdminAddress(); SendPackage(address); } 

Thus preserving encapsulation and eliminating the need for null checks. Using the operator "?." may mask encapsulation problems . It is better not to be tempted to use the operator "?." in such cases, even if “linking” method calls one after another can seem like a very simple task.

Valid Usage Examples


In what cases the use of the operator "?." is it permissible? First of all, it's a legacy code. If you are working with a code or library to which you do not have access (or simply do not want to touch the source code), you may have no other choice but to adapt to this code. In this case, the operator "?." can help reduce the amount of code needed to work with a similar code base.

Another good example is event invocation:

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

The remaining examples are reduced to those that do not fall under the three options for invalid use described above.

Conclusion


While the operator "?." can help reduce the amount of code in some cases, it can also disguise signs of poor design (design smells) that could be more obvious without it.

In order to decide whether or not to use this operator in a particular case, just think whether the code written “in the old manner” will be valid. If so, feel free to use this operator. Otherwise, try to refactor the code and remove the flaws in the design.

The English version of the article: 3 misuses of "?." operator in C # 6

Source: https://habr.com/ru/post/269285/


All Articles