C # 中是否有一个“空列表”单例?

在 C # 中我使用了 LINQ 和 IEnumable,而且一切都很好(至少大部分是这样)。

然而,在许多情况下,我发现自己需要一个空的 IEnumerable<X>作为默认值。也就是说,我想

for (var x in xs) { ... }

不需要空检查就可以工作。现在这是我目前所做的,取决于更大的上下文:

var xs = f() ?? new X[0];              // when xs is assigned, sometimes
for (var x in xs ?? new X[0]) { ... }  // inline, sometimes

现在,虽然上面的代码是 对我来说完全没问题——也就是说,如果在创建数组对象 I 我不在乎时有任何“额外开销”——我想知道:

C #/中是否存在“空的不可变 IEnumable/IList”单例。NET? (即使没有,是否有“更好”的方法来处理上述情况?)

Java 具有 Collections.EMPTY_LIST不可变的单例——通过 Collections.emptyList<T>()实现的“良好类型”——这就达到了这个目的,尽管我不确定类似的概念是否可以在 C # 中工作,因为泛型的处理是不同的。

谢谢。

46628 次浏览

Enumerable.Empty<T>() is exactly that.

I think you're looking for Enumerable.Empty<T>().

Empty list singleton doesn't make that much sense, because lists are often mutable.

You are looking for Enumerable.Empty<T>().

In other news the Java empty list sucks because the List interface exposes methods for adding elements to the list which throw exceptions.

I think adding an extension method is a clean alternative thanks to their ability to handle nulls - something like:

  public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> list)
{
return list ?? Enumerable.Empty<T>();
}


foreach(var x in xs.EmptyIfNull())
{
...
}

Microsoft implemented `Any()' like this (source)

public static bool Any<TSource>(this IEnumerable<TSource> source)
{
if (source == null) throw new ArgumentNullException("source");
using (IEnumerator<TSource> e = source.GetEnumerator())
{
if (e.MoveNext()) return true;
}
return false;
}

If you want to save a call on the call stack, instead of writing an extension method that calls !Any(), just rewrite make these three changes:

public static bool IsEmpty<TSource>(this IEnumerable<TSource> source) //first change (name)
{
if (source == null) throw new ArgumentNullException("source");
using (IEnumerator<TSource> e = source.GetEnumerator())
{
if (e.MoveNext()) return false; //second change
}
return true; //third change
}

Using Enumerable.Empty<T>() with lists has a drawback. If you hand Enumerable.Empty<T> into the list constructor then an array of size 4 is allocated. But if you hand an empty Collection into the list constructor then no allocation occurs. So if you use this solution throughout your code then most likely one of the IEnumerables will be used to construct a list, resulting in unnecessary allocations.

In your original example you use an empty array to provide an empty enumerable. While using Enumerable.Empty<T>() is perfectly right, there might other cases: if you have to use an array (or the IList<T> interface), you can use the method

System.Array.Empty<T>()

which helps you to avoid unnecessary allocations.

Notes / References: