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


79

在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#中使用,因为泛型的处理方式不同。

谢谢。


好吧,该死的:)这就是我专注于List / IList而不是Enumerable / IEnumerable的目的,谢谢大家-大家都投了赞成票。


2
public static class Array<T> { public static readonly T[] Empty = new T[0]; }它可以这样调用:Array<string>.Empty。我在CodeReview中询问了此问题
Şafak古尔

Answers:


97

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

在其他消息中,Java空列表很烂,因为List接口公开了用于向列表中添加引发异常的元素的方法。


这是非常正确的一点!也许值得提出一个新的SO问题?

1
您慢了几秒钟,并且没有文档链接* finger晃动* ;-)但是,在svicks评论上扩展的接受度。

另外,其他人也有更多的反对意见,因此我们什至:)我会发一个问题,但我现在要睡觉了,我将无法关注这个问题。为什么不自己张贴呢?
Stilgar 2011年

32
因为类方法可能需要返回List(可以通过索引访问的集合)。这些List可以是只读的。而且有时您需要返回这样的只读List元素,其中包含零个元素。因此,该Collections.emptyList()方法。您不能只返回一个空的Iterable,因为已实现的接口指定该方法返回一个列表。最大的问题是它们不是可以返回的ImmutableList接口。.NET中存在相同的问题:任何问题都IList可能是只读的。
Laurent Bourgault-Roy 2012年

6
附带说明一下,每个数组都是C#中的IList <correspondingType>,当您尝试添加新项目时也会抛出该异常。
Grzenio 2014年

52

Enumerable.Empty<T>() 就是这样


3
好吧,不完全是。:)由于要求使用“列表”,Array.Empty<T>()因此更准确,因为它是IList<T>。它也具有令人满意的幸福好处IReadOnlyCollection<T>。当然,只要一个IEnumerable<T> 足够,就Enumerable.Empty<T>()非常适合。
Timo '18

2
@Timo这个问题以及包括这个问题在内的许多答案大约Array.Empty<T>是在发布3年之前写的。:)在任何情况下,尽管标题如此,但该问题仍清楚地表明,“ an IEnumerable<T>”对目标有用。
乔恩·


12

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

System.Array.Empty<T>()

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

注释/参考:


10

我认为添加扩展方法是一种干净的替代方法,这是由于它们具有处理null的能力-类似于:

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

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

1

Microsoft像这样实现了“ 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
}

1

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.