26

新的 C# 6.0 空条件运算符是编写更简洁和更少复杂代码的便捷工具。假设一个客户有一组客户,那么如果为 null ,则可以使用此方法获得 null 而不是长度(来自MSDNcustomers的示例):

int? length = customers?.Length;

同样,您可以通过以下方式获得 null 而不是客户:

Customer first = customers?[0];

对于更详细的表达式,如果customers为 null、第一个客户为 null 或第一个客户的Orders对象为 null,则生成 null:

int? count = customers?[0]?.Orders?.Count();

但是还有一个有趣的案例,即空条件运算符似乎没有解决的不存在的客户。我们在上面看到覆盖了一个客户,即如果customers数组中的条目为空。但这与不存在的客户截然不同,例如5在 3 元素数组中查找客户或n在 0 元素列表中查找客户。(请注意,同样的讨论也适用于字典查找。)

在我看来,空条件运算符只专注于否定 NullReferenceException 的影响;IndexOutOfRangeException 或 KeyNotFoundException 是孤独的,暴露的,蜷缩在角落里,需要自生自灭!我认为,本着空条件运算符的精神,它也应该能够处理这些情况......这导致了我的问题。

我错过了吗?空条件是否提供任何优雅的方式来真正覆盖这个表达式......

customers?[0]?.Orders?.Count();

...当没有第零个元素时?

4

4 回答 4

35

不,因为它是一个null条件运算符,而不是一个indexoutofrange 条件运算符,并且只是类似于以下内容的语法糖:

int? count = customers?[0]?.Orders?.Count();

if (customers != null && customers[0] != null && customers[0].Orders != null)
{
    int count = customers[0].Orders.Count();
}

您可以看到,如果没有第零个客户,您将获得您的常客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();
于 2016-05-05T00:44:51.190 回答
14
customers?.FirstOrDefault()?.Orders?.Count();

没有零点,没问题。

于 2016-05-05T01:38:09.620 回答
8

如果您想在没有 NullReference 或 IndexOutOfRange 异常的情况下获取第 n 个元素,您可以使用:

customers?.Skip(n)?.FirstOrDefault()
于 2019-10-15T17:38:59.063 回答
3

它不支持索引安全性,因为当您深入了解它时,索引器实际上只是任何其他类型方法的语法糖。

例如:

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 为空时抛出的空引用异常。

如果我们要捕获索引查找异常、未找到键异常等,也会出现同样的问题。

于 2016-05-05T00:55:51.880 回答