在 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”单例?(即使没有,是否有“更好”的方法来处理上述情况?)
Collections.EMPTY_LIST
不可变的单例——通过 Collections.emptyList<T>()
“类型正确”——它可以达到这个目的,尽管我不确定类似的概念是否可以在 C# 中工作,因为泛型的处理方式不同。
谢谢。
Enumerable.Empty<T>()
正是如此。
在原始示例中,您使用空数组来提供空枚举。虽然使用
Enumerable.Empty<T>()
是完全正确的,但可能还有其他情况:如果您 必须使用 数组(或 IList<T>
接口),则可以使用该方法
System.Array.Empty<T>()
这可以帮助您避免不必要的分配。
注释/参考文献:
我认为添加扩展方法是一个干净的替代方案,因为它们能够处理空值 - 例如:
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> list)
{
return list ?? Enumerable.Empty<T>();
}
foreach(var x in xs.EmptyIfNull())
{
...
}
将
Enumerable.Empty<T>()
与列表一起使用有一个缺点。如果将 Enumerable.Empty<T>
交给列表构造函数,则会分配一个大小为 4 的数组。但是,如果您将空的 Collection
传递给列表构造函数,则不会发生分配。因此,如果您在整个代码中使用此解决方案,那么很可能会使用其中一个 IEnumerable
来构造列表,从而导致不必要的分配。
微软像这样实现了 `Any()' (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;
}
如果您想在调用堆栈上保存调用,则不必编写调用
!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
}