What is the "?." operator?

22

I'm seeing the functions that C # 6.0 brought, and an example of code appeared:

C # 5.0

var vendor = new Company();
var location = default(string);
if (vendor != null)
  if (vendor.ContactPerson != null)
    if (vendor.ContactPerson.HomeAddress != null)
      location = vendor.ContactPerson.HomeAddress.LineOne;
Console.WriteLine(location);

C # 6.0

var vendor = new Company();
var location = vendor?.ContactPerson?.HomeAddress?.LineOne;
WriteLine(location);

Both have the same functionality, but in the second example everything is done using an inline conditional.

Inline if with two values is easy, but I do not quite understand how this works.

    
asked by anonymous 11.10.2015 / 03:26

2 answers

18

This is the conditional access or null propagation or safe navigation operator. I have not yet chosen which one to use :) But I think the second one is better.

As was so often the check if an object was null before doing any operations (as shown in the first example), it was so much used design pattern, it would be useful if the language had a facility for it .

Note that access to any member of the object can be "protected" with this operator. Methods can also be called. Obviously the call will only occur if the object is not null.

The result of an expression objeto ?. membro will be null , if objeto is null . And since the expressions can be chained, the first null that it finds all other subexpressions found next will also null . He will not even try to evaluate the rest, the end result will be null . And this is important, it is a short circuit operation, that is, it does not even try to run the rest when there is nothing else to do.

The two codes presented in the question are exactly equivalent. In this case, it will coincidentally be a specific case rarely used where default is a null . In this case the initialization of the string location is a null in any situation. But think that if it had been initialized with "" , that is, if it were an empty string , it would be different.

In the first to the end of the operation if vendor is null, the contents of location will not be changed, ie, it will be "" . In the second code it will be null , after all the result of an operation with an object null is null .

Let's see more about the operator

This code will cause a exception on the last line for trying to access a null object:

string texto = null;
string parte = "";
parte = texto?.Substring(0, 1);
WriteLine($"{parte.Length}");

Obviously we could have used the ?. operator before Length and avoid the exception.

If you add this line:

int tamanho = parte.Length;

Generates an exception because it can not take the size of a null object. Right? So let's do this:

int tamanho = parte?.Length;

Resolve?

Neither compile. After all, now the expression possibly results in a null. A int can not be null. So we would have to do this:

int? tamanho = parte?.Length;

A nullable integer can contain all possible results of this expression, including the value null .

Except in this case you are using a different type than you normally use just to solve a problem that did not exist. It's better to do it the old way:

int tamanho = parte != null ? parte.Length : 0;

or merging old with new:

int tamanho = parte?.Length ?? 0;

Note that the same operator may have different syntax in another context:

Cliente cliente = clientes?[0]; //isto também é o operador de propagação de nulo

Another example of simplification is in checking if an object has a member:

(objeto as IExemplo)?.Exemplo();

If objeto does not implement this interface IExemplo , the result of the operation will be null, so the Exemplo() method will not be called and will not cause an error.

In thesis would help programmers be more careful with code. There is a lot of code out there that does not give null reference error by coincidence. One day, it falls into the situation that the object is null and the application breaks without the unwary realize. Although I do not know if it will help a lot because it has a lot of programmer who does not know the "news" of C # 3, and some even of C # 2. And obviously do not even know everything that already existed in C # 1. It will also happen that the person thinks this solves everything, which must be used everywhere, without thinking. The member access operator . or [] must still be the default access operator.

Thread-safe

Its use may be more thread-safe in some cases by using a temporary variable to store the result of the expression. Example of gain:

//necessário para ser thread-safe
EventHandler<decimal> precoOnChanged = OnPrecoChanged;
if (precoOnChanged != null) {
    preco = value;
    precoOnChanged (this, value);
}

Turns into:

preco = value;
OnPrecoChanged?.Invoke(this, value) //Invoke é usado p/ resolver ambiguidade c/ delegate

Prevent the null from occurring

A feature of .Net 4 that people ignore is the use of contracts. With it you can neither need this and avoid checks against null at runtime, detecting at development time:

public int ProcuraTeste(string texto) { //não compila se texto for null
    Contract.Requires(texto != null); //isto pode ser eliminado do runtime
    return texto.IndexOf("teste");
}

Of course this is no solution at all. There are cases that can only be known at runtime, that is, the null is something expected as a valid value. There is a lot of confusion about what a null is . If it were invalid, the type system, compiler, or static analysis should detect and prevent this.

Simplifying without this operator

As additional information, in C # 5 you can simplify the code a bit:

if (vendor != null && vendor.ContactPerson != null && vendor.ContactPerson.HomeAddress != null)
    location = vendor.ContactPerson.HomeAddress.LineOne;
    
11.10.2015 / 16:11
9

This code is not about inline if s , but on cascading% evaluations.

In the example above, the developer wants to get the value of NULL .

However, several properties need to be tested by vendor.ContactPerson.HomeAddress.LineOne values: null , vendor , ContactPerson and - finally - HomeAddress .

In C # 5 or lower, you would need to implement a test for each object.

With the new syntax, you can now evaluate NULL string in a single line.

    
11.10.2015 / 03:28