88

在 C# 中,我经常使用 LINQ 和 IEnumerable。一切都很好(或至少大部分如此)。

但是,在许多情况下,我发现自己需要一个空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

现在,虽然上述内容对我来说非常好——也就是说,如果创建数组对象有任何“额外开销”我不在乎——我想知道:

C#/.NET 中是否有“空的不可变 IEnumerable/IList”单例?(而且,即使没有,是否有一种“更好”的方式来处理上述情况?)

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

谢谢。

4

7 回答 7

105

您正在寻找Enumerable.Empty<T>().

在其他新闻中,Java 空列表很糟糕,因为 List 接口公开了向列表添加元素的方法,这会引发异常。

于 2011-12-19T00:08:35.130 回答
55

Enumerable.Empty<T>()正是这样。

于 2011-12-19T00:07:34.070 回答
20

我想你正在寻找Enumerable.Empty<T>().

空列表单例没有多大意义,因为列表通常是可变的。

于 2011-12-19T00:08:19.997 回答
17

在您的原始示例中,您使用一个空数组来提供一个空的可枚举。虽然 usingEnumerable.Empty<T>()完全正确,但可能还有其他情况:如果必须使用数组(或IList<T>接口),则可以使用方法

System.Array.Empty<T>()

这可以帮助您避免不必要的分配。

注释/参考:

于 2018-05-15T20:41:59.563 回答
10

我认为添加一个扩展方法是一个干净的选择,这要归功于它们处理空值的能力——比如:

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

  foreach(var x in xs.EmptyIfNull())
  {
    ...
  }
于 2011-12-19T22:19:04.283 回答
1

微软像这样实现了“Any()”(来源

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;
}

如果要在调用堆栈上保存调用,而不是编写调用的扩展方法!Any(),只需重写进行以下三个更改:

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
}
于 2015-12-07T16:53:55.103 回答
1

与列表一起使用Enumerable.Empty<T>()有一个缺点。如果您Enumerable.Empty<T>进入列表构造函数,则分配一个大小为 4 的数组。但是,如果您将一个空Collection输入到列表构造函数中,则不会发生分配。因此,如果您在整个代码中使用此解决方案,那么很可能其中一个IEnumerables 将用于构造列表,从而导致不必要的分配。

于 2017-07-30T04:10:25.330 回答