用法 ??运算符(零合并运算符)

本文关键字:运算符 合并 用法 | 更新日期: 2023-09-27 18:30:48

我在代码中大量使用 ?? 运算符。但是今天我刚刚遇到了一个问题。

这里是我用于??运算符

的代码
private List<string> _names;
public List<string> Names
{
    get { return _names ?? (_names = new List<string>()); }
}

但在某些地方,我也看到了这段代码。

private List<string> _names;
public List<string> Names
{
    get { return _names ?? new List<string>(); }
}

这些代码之间的真正区别是什么。在一个我分配_names = new List(),而在另一个中我只是在做新的 List()。

用法 ??运算符(零合并运算符)

我看到的唯一区别是,在第一种情况下,您的变量_names将包含一个新的空列表,而在第二种情况下则不包含。在这两种情况下,返回的值将是相同的。

在第二种情况下,您不会为_names分配new List<string>(),如果_names为null,则每次调用Names时都会创建new List<string>(),但在第一种情况下new List<string>()创建。

它在两种情况下都会返回相同的值,但不同之处在于,如果您在私有函数中的任何位置访问_names_names在第一种情况下将有一个空列表,但在第二种情况下将具有空值。因此,根据编码标准,第一个实现是正确的,在第二种情况下,您可能会遇到空异常问题。

正如其他人所说,区别在于检索属性后字段_names的状态。

案例1

get { return _names ?? (_names = new List<string>()); }

如果_names为 null,则将创建并分配新的List<string>。 在此之后,每次检索属性Names都将返回刚刚创建的列表。

案例2

get { return _names ?? new List<string>(); }

如果_names为 null,则将返回新的List<string>。 与情况 1 相反,它不会被分配给任何东西。 这意味着每次检索Names时,都会创建一个新列表并返回。


话虽如此,您应该非常小心此代码的两个版本。 在get中分配值不一定是一件好事。 延迟实例化属性值很方便,如果您是唯一使用该类的人,这可能是可以接受的,但它的样式很差。 您类之外的任何用户都不会期望get设置任何内容......这就是房产set部分的用途。 从长远来看,您最好只返回_names并在默认构造函数中实例化它。 然后,就清楚代码在做什么,并实现有保证的非 null 属性值。

对于第二种情况,最好返回 null 并允许用户处理它。 请考虑以下情况,其中items是大型对象集合,每个对象都包含属性Names,并且无论出于何种原因,Names始终为 null:

foreach (var item in items.Where(x => x.Names != null)) {
    Console.WriteLine(String.Join(", ", item.Names));
}

如果您只是返回 null,则将跳过整个块。 但是,由于您每次都返回一个新列表,因此您不仅要运行整个循环并且基本上什么都不做,而且要在每次迭代中实例化一个新列表! 在某些情况下,这可能会变得非常昂贵。