??运算符的使用(空值合并运算符)

3

我在我的代码中经常使用 ?? 运算符。但今天我遇到了一个问题。

以下是我使用 ?? 运算符的代码:

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(),而在另一个代码中,我只是做了new List()。

1
这两个非常不同,第二个可能是一个错误。 - Igby Largeman
为什么不直接初始化成员变量呢?这样代码会更清晰。private List<string> _names = new List<string>();. - jgauffin
4个回答

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

这意味着每次都会返回一个新列表,如果我调用Names.Add("SomeName"),然后调用Names.Count,我会得到0而不是1,对吗? - fhnaseer
确切地说,在第二种情况下。 - user786981

3
在第二种情况下,你没有使用new List<string>()来赋值给_names。如果_names为空,在每次调用Names时都会创建new List<string>(),但在第一种情况下,new List<string>()只会被创建一次。

这意味着每次都会返回一个新列表,如果我调用Names.Add("SomeName"),然后调用Names.Count,我会得到0而不是1,对吗? - fhnaseer
@FaisalHafeez 是的,但只在第二种情况下。 - Damith

2

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

情况1

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

如果 _names 为空,将创建并分配一个新的 List<string>。之后,每次检索属性 Names 将返回刚创建的列表。
get { return _names ?? new List<string>(); }

如果_names为null,则将返回一个新的List<string>。与第1种情况相比,它不会被分配给任何内容。这意味着每次检索Names时,都会创建并返回一个新列表。
话虽如此,你应该非常小心处理这两个版本的代码。在get中分配值不一定是件好事。在属性值上进行惰性实例化很方便,也许在你是唯一使用该类的情况下可以接受,但这是一种糟糕的风格。没有你类外的用户会期望get设置任何内容...这就是属性的set部分的作用。从长远来看,最好只返回_names并在默认构造函数中进行实例化,这样就清楚了代码的含义,并且可以确保获得非空属性值。
对于第二种情况,最好返回null并允许用户自行处理。考虑以下情况:其中items是包含您的属性Names的大型对象集合,而无论出于什么原因,Names始终为null:
foreach (var item in items.Where(x => x.Names != null)) {
    Console.WriteLine(String.Join(", ", item.Names));
}

如果你仅仅返回null,那么整个代码块将被跳过。然而,由于你每次都返回一个新的列表,不仅会遍历整个循环并且基本上没有任何作用,还会在每次迭代时实例化一个新的列表!在某些情况下,这可能会非常昂贵。

2

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接