What is it? Here’s the scenario
Consider getting the grandchild of a parent object like this:
var g1 = parent.child.child.child;
很明显parent类里面有一个child类的成员,child类又包含child类的成员,这是一个递归调用,child.child.child....可以延伸到无限级。
Okay, so, this is some poor coding because the value of child could be null. If I attempt to ask for the value of child and the containing object is null, a NullReferenceException will be raised and my app will crash.
Here’s what I mean. Consider this:
var g1 = [parent].[child].[null].child;
In this case, I was lucky enough to get two levels in before I hit a null object. But I did hit a null object, and as a result the runtime will thrown an exception.
Instead, you might add some error checking and do this:
// variation 1 var item = this.Parent; item = (item == null) ? null : item.child; item = (item == null) ? null : item.child; var g1 = (parent == null) ? null : item.child; if (g1 != null) // TODO
// variation 2 var g1 = (Child)null; var item = this.Parent; if (item != null) { item = item.Child; if (item != null) { item = item.Child; if (item != null) { g1 = item.Child; } } } if (g1 != null) // TODO
Good. Now, this is safe and effective coding. The sample above shows two of many potential approaches. The first using the ternary(三元的) operator in C#, the second using simple conditional blocks. As we know, we cannot ask for a child property from an object that is null. So, we must check each level.
The conditional code is not difficult to write, but it certainly impacts the size of your code, the complexity of your code, and the readability of your code. Having said that, it’s what we all do today. Code like this shows up all the time and there is nothing wrong with that; but, what if there were a better way.
How the new operator works
Consider getting the grandchild of a parent object like this:
var g1 = parent?.child?.child?.child; if (g1 != null) // TODO
Wow! That’s the equivalent of testing every single level, but in a single line of code. The “?.” operator is basically saying, if the object to the left is not null, then fetch what is to the right, otherwise return null and halt the access chain.
It’s a wonderful addition to the language’s syntax.
Furthermore
Mad’s Visual Studio User Voice comment continued with a little more explanation of the operator’s implementation. He said, “If the type of e.x (etc) is a non-nullable value type S, then the type of e?.x is S?. Otherwise the type of e?.x is the same as that of e.×. If we can’t tell whether the type is a non-nullable value type (because it is a type parameter without sufficient constraints) we’ll probably give a compile-time error.” This comment, and the idea that a method call or indexer can be to the right of the operator are just candy.
> Now, let’s add NonNullable reference types and we’re really cooking.
此外如果使用!运算符声明类为NonNullable类型,再使用?.运算符时会导致编译错误,如下所示:
public void DoSomething(Order! order) { Customer customer = order?.Customer; // Compiler error: order can’t be null }
因为这时order对象永远不可能为null,C#编译器认为在order后使用?.是多此一举