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;