用 Enumerable.Empty<T>() 返回空集合更轻量、语义更清晰:它返回静态缓存的单例实例,零分配、零GC压力,且明确表达“空只读序列”意图;而 new List<T>() 和 new T[0] 均会重复分配对象,且语义模糊。

直接说结论:用 Enumerable.Empty<T>() 返回空集合,比 new List<T>() 或 new T[0] 更轻量、更语义清晰,且能避免不必要的对象分配。
为什么不用 new List<T>() 或 new T[0]?
很多人习惯写 return new List<string>(); 或 return new string[0];,但它们有隐含成本:
-
new List<T>()每次都新建一个可变容器对象,哪怕它为空,也占内存、触发 GC 压力 -
new T[0]虽然数组不可变,但每次调用仍分配新数组对象(.NET 不会自动复用零长度数组) - 两者都不表达“我明确要一个空的、只读的、延迟兼容的序列”这层意图
Enumerable.Empty<T>() 的实际行为和适用场景
Enumerable.Empty<T>() 返回的是一个静态缓存的、类型安全的空 IEnumerable<T> 实例——内部是单例,不分配新对象,且实现了 ICollection<T> 和 IReadOnlyCollection<T>。
- 适合所有需要返回“空序列”的公开 API,比如过滤后无结果、配置未启用时的默认值
- 与 LINQ 链式调用天然兼容:
source.Where(...).Concat(Enumerable.Empty<int>()) - 在异步流(
IAsyncEnumerable<T>)中不适用,此时应改用AsyncEnumerable.Empty<T>()(需System.Linq.Async包) - 不能直接当
List<T>用——它不支持Add、Count等方法(除非显式转型,但没必要)
常见误用和性能陷阱
看似简单,但容易踩坑:
- 写成
Enumerable.Empty(漏掉泛型参数)→ 编译错误:The non-generic type 'Enumerable' cannot be used with type arguments - 在循环里反复调用
Enumerable.Empty<int>()→ 没问题,它是静态方法,返回同一实例,无性能损失 - 想用它初始化字段或属性时写成
private readonly IEnumerable<string> _empty = Enumerable.Empty<string>();→ 完全正确,且推荐 - 误以为它等价于
null→ 它是非 null 的有效集合,foreach安全,.Any()返回false,.Count()返回0
最常被忽略的一点:如果你的函数签名返回的是 IList<T>> 或 ICollection<T>,就不能直接返回 Enumerable.Empty<T>()——它不实现这些接口(虽然 .NET 6+ 的 EmptyEnumerable<T> 内部已实现 IReadOnlyCollection<T>,但依然不是 ICollection<T>)。这时候要么改返回类型为 IEnumerable<T>,要么退而求其次用 Array.Empty<T>()(它返回 T[],实现了全部集合接口,且也是静态缓存)。


















