C# 中是不是有“空列表”单例?
Posted
技术标签:
【中文标题】C# 中是不是有“空列表”单例?【英文标题】:Is there an "Empty List" singleton in C#?C# 中是否有“空列表”单例? 【发布时间】:2012-01-23 06:15:19 【问题描述】:在 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”单例?(即使没有,是否有“更好”的方法来处理上述情况?) p>
Java 有 Collections.EMPTY_LIST
不可变单例——通过 Collections.emptyList<T>()
“良好类型化”——它可以达到这个目的,尽管我不确定类似的概念是否甚至可以在 C# 中工作,因为泛型的处理方式不同。
谢谢。
【问题讨论】:
好吧,该死的 :) 这就是我关注 List/IList 而不是 Enumerable/IEnumerable 的结果,谢谢大家 -- 投票。 ***.com/questions/3229698/…public static class Array<T> public static readonly T[] Empty = new T[0];
可以这样调用:Array<string>.Empty
。我问了here in CodeReview。
【参考方案1】:
Enumerable.Empty<T>()
就是这样。
【讨论】:
嗯,不完全是。 :) 由于要求“列表”,Array.Empty<T>()
更准确,因为它是IList<T>
。它还具有满足IReadOnlyCollection<T>
的快乐好处。当然,IEnumerable<T>
确实 就足够了,Enumerable.Empty<T>()
非常适合。
@Timo 这个问题以及包括这个问题在内的许多答案都是在Array.Empty<T>
可用之前大约 3 年写的。 :) 无论如何,尽管有标题,但问题清楚地表明IEnumerable<T>
有利于达到目的。【参考方案2】:
我想你正在寻找Enumerable.Empty<T>()
。
空列表单例没有多大意义,因为列表通常是可变的。
【讨论】:
【参考方案3】:您正在寻找Enumerable.Empty<T>()
。
在其他新闻中,Java 空列表很糟糕,因为 List 接口公开了向列表添加元素的方法,这会引发异常。
【讨论】:
这是一个非常有效的观点!也许它需要一个新的 SO 问题? 你慢了几秒钟,没有文档链接 *finger shake* ;-) 但是接受扩展 svicks 评论。 另外,其他人得到了更多的支持,所以我们平分秋色:) 我会发布一个问题,但我现在要睡觉了,我将无法关注这个问题。为什么不自己发布呢? 因为一个类方法可能需要返回一个List
(可以通过索引访问的集合)。那些List
可以是只读的。有时您需要返回这样一个只读的List
,其中包含零元素。因此,Collections.emptyList()
方法。您不能只返回一个空的 Iterable,因为实现的接口指定该方法返回一个列表。最大的问题是它们没有可以返回的 ImmutableList 接口。 .NET 中也存在同样的问题:任何 IList
都可能是只读的。
附带说明,每个数组都是 C# 中的 IList我认为添加一个扩展方法是一个干净的选择,这要归功于它们处理空值的能力——比如:
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> list)
return list ?? Enumerable.Empty<T>();
foreach(var x in xs.EmptyIfNull())
...
【讨论】:
【参考方案5】:Microsoft 像这样实现了 `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
【讨论】:
【参考方案6】:将Enumerable.Empty<T>()
与列表一起使用有一个缺点。如果您将Enumerable.Empty<T>
交给列表构造函数,则会分配一个大小为 4 的数组。但是,如果您将一个空的Collection
交给列表构造函数,则不会发生分配。因此,如果您在整个代码中使用此解决方案,那么很可能会使用 IEnumerable
s 之一来构造一个列表,从而导致不必要的分配。
【讨论】:
【参考方案7】:在您的原始示例中,您使用一个空数组来提供一个空的可枚举。虽然使用Enumerable.Empty<T>()
是完全正确的,但可能还有其他情况:如果您必须使用一个数组(或IList<T>
接口),您可以使用该方法
System.Array.Empty<T>()
这可以帮助您避免不必要的分配。
注释/参考:
documentation 没有提到该方法为每个类型只分配一次空数组 roslyn analyzers 建议使用此方法并带有警告 CA1825: Avoid zero-length array allocations 微软reference implementation .NET Core implementation【讨论】:
感谢您指出文档与 roslyn 分析器不一致。你有没有在 GitHub 上打开一个问题来纠正这个问题?以上是关于C# 中是不是有“空列表”单例?的主要内容,如果未能解决你的问题,请参考以下文章