This is the operator conditional access or null Propagation or safe navigation. I haven’t decided which to use yet :) But I think the second is better.
As it was so frequent to check whether an object is null before doing some operation (as shown in the first example), it was a design pattern so used, it would be useful if the language had a facility for this.
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 the objeto
be it null
. And how expressions can be chained, the first null
that he find all the other sub-expressions found below will be worth null
also. He won’t even try to evaluate the rest, the end result will be null
. And this is important, it’s a short-circuit operation, which means it doesn’t even try to perform the rest when there’s nothing else to do.
The two codes presented in the question nay are exactly equivalent. In this case it will even be, by coincidence, because it is a specific case rarely used where one wants as a result default one null
. In this case the initialization of string location
is a null
in any situation. But think that if it had been initialized with ""
, that is, if it were a string empty, would be different.
On the first at the end of the operation if vendor
is null, the content of location
will not be changed, that is, it will be worth ""
. In the second code it will be worth 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 an exception in the last row 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 the Length
and avoid the exception.
If you add this line:
int tamanho = parte.Length;
It makes an exception because you can’t get the size of a null object. Right? So let’s do this:
int tamanho = parte?.Length;
Resolves?
Neither compiles. After all now the expression possibly results in a null. A int
cannot be null. Then we would have to do this:
int? tamanho = parte?.Length;
An voidable integer can contain all possible results of this expression, including the value null
.
Only in this case, you’re using a different type than what you normally use just to solve a problem that didn’t exist. Then you better do it the old-fashioned way:
int tamanho = parte != null ? parte.Length : 0;
or by blending the old with the 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 whether an object has a member:
(objeto as IExemplo)?.Exemplo();
If the objeto
does not implement this interface IExemplo
, the result of the operation will be null, therefore the method Exemplo()
will not be called and will not cause error.
In thesis it would help programmers be more careful with code. There is a lot of code out there that does not give error of null Reference by coincidence. One day, it falls into the situation that the object is null and the application breaks without the unwary realizing. Although I don’t know if it will help much because there are many programmers who don’t know the "news" of C# 3, and some even of C# 2. And obviously you don’t know everything that already existed in C# 1. It will also happen that people think that this solves everything, that you should use everywhere, thoughtlessly. The operator accessing the member .
or []
must still be the standard access operator.
Thread-safe
Its use may be more thread-safe in some cases by using a temporary variable to store the output 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 happening
A . NET 4 resource that people ignore is the use of contracts (it has been abandoned lately). With it it is possible not to need it and avoid countercheck 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");
}
I put in the Github for future reference.
Of course this is not the solution for everything. There are cases that you can only know at runtime, that is, null is something expected as a valid value. There’s a lot of confusion about what a zero is. If it was 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 little:
if (vendor != null && vendor.ContactPerson != null && vendor.ContactPerson.HomeAddress != null)
location = vendor.ContactPerson.HomeAddress.LineOne;
Interesting question of contracts. Just a question, how long has it been that program in C#/. NET
– Ezequiel Barbosa
Complicated answer. 14 years? or 0 years?
– Maniero
I think this question of the term is important, sometimes languages have a different term for exactly the same behavior, but because of the context of the language the term changes. In Ruby it is called Safe Navigation, as there is no null there (only nil), it does not make sense the term Null Propagation, whereas in C# it makes much more sense, it is more explicit that the type of return will be a Nullable.
– Gabriel Katakura