新的 C# 6.0 空条件运算符是编写更简洁、更少复杂的代码的便捷工具。假设有一个客户数组,那么如果customers
是空的,则可以使用以下命令获得 null 而不是长度(MSDN 中的示例(:
int? length = customers?.Length;
同样,您可以通过以下方式获得 null 而不是客户:
Customer first = customers?[0];
对于更精细的表达式,如果customers
为 null、第一个客户为 null 或第一个客户的 Orders
对象为 null,则生成 null:
int? count = customers?[0]?.Orders?.Count();
但是,还有一个有趣的案例,即不存在的客户,空条件运算符似乎没有解决。我们在上面看到,覆盖了一个空客户,即如果customers
数组中的条目为空。但这与不存在的客户截然不同,例如,在 3 元素数组中查找客户5
或在 0 元素列表中查找客户n
。(请注意,同样的讨论也适用于字典查找。
在我看来,空条件运算符只专注于否定 NullReferenceException 的影响;IndexOutOfRangeException或KeyNotFoundException是孤独的,暴露的,畏缩在角落里,需要自生自灭!我认为,本着空条件运算符的精神,它也应该能够处理这些情况......这就引出了我的问题。
我错过了吗?null 条件是否提供了任何优雅的方式来真正覆盖此表达式......
customers?[0]?.Orders?.Count();
。当没有第零个元素时?
不,因为它是一个空条件运算符,而不是索引范围条件运算符,并且只是类似以下内容的语法糖:
int? count = customers?[0]?.Orders?.Count();
if (customers != null && customers[0] != null && customers[0].Orders != null)
{
int count = customers[0].Orders.Count();
}
您可以看到,如果没有第 0 个客户,您将获得常规IndexOutOfRangeException
。
解决方法之一是使用一个扩展方法检查索引,如果不存在,则返回 null:
public static Customer? GetCustomer(this List<Customer> customers, int index)
{
return customers.ElementAtOrDefault(index); // using System.Linq
}
那么你的支票可以是:
int? count = customers?.GetCustomer(0)?.Orders?.Count();
customers?.FirstOrDefault()?.Orders?.Count();
没有零,没问题。
如果要在没有 NullReference 或 IndexOutOfRange 异常的情况下获取第 n 个元素,可以使用:
customers?.Skip(n)?.FirstOrDefault()
它不支持索引安全性,因为当你深入了解它时,索引器实际上只是任何其他类型的方法的语法糖。
例如:
public class MyBadArray
{
public Customer this[int a]
{
get
{
throw new OutOfMemoryException();
}
}
}
var customers = new MyBadArray();
int? count = customers?[5]?.Orders?.Count();
这应该在这里被抓住吗?如果异常更明智,类似于 KeyNotFoundException,但特定于我们正在实现的集合类型,该怎么办?我们必须不断更新?.
功能才能跟上。
此外,?.
不会捕获异常。它可以防止它们。
var customer = customers?[5];
实际上编译为:
Customer customer = null;
if (customers != null)
customer = customers[5];
让它捕获异常变得异常困难。例如:
void Main()
{
var thing = new MyBadThing();
thing.GetBoss()?.FireSomeone();
}
public class MyBadThing
{
public class Boss
{
public void FireSomeone()
{
throw new NullReferenceException();
}
}
public Boss GetBoss()
{
return new Boss();
}
}
如果它只是捕获异常,它将写为:
Boss boss = customer.GetBoss();
try
{
boss.FireSomeone();
} catch (NullReferenceException ex) {
}
这实际上会捕获FireSomeone
内的异常,而不是如果 boss 为空就会抛出的空引用异常。
如果我们要捕获索引查找异常、找不到键异常等,也会出现同样的错误捕获问题。