Prepend和Append是LINQ扩展方法,返回新IEnumerable而不修改原集合;需.ToList()等显式转换才能获得新集合,适用于函数式链式处理与临时拼接。

Prepend 和 Append 是扩展方法,不是 List 自带的
它们属于 System.Linq 命名空间下的扩展方法,只作用于 IEnumerable,不会修改原集合,而是返回新序列。常见错误是以为能直接改 List 或 Array —— 实际上你调用后得到的是一个 IEnumerable,得显式转成 List 或其它类型才能继续操作。
使用场景主要是函数式链式处理、临时拼接数据流,比如日志前加标题、API 返回结果末尾补默认项。
-
Prepend在序列开头插入一个元素,原序列在后 -
Append在序列末尾插入一个元素,原序列在前 - 两者都返回
IEnumerable,不触发立即执行(延迟执行) - 如果要立刻生成新列表,得接
.ToList()或.ToArray()
为什么 Prepend/Append 后原集合没变?
因为 LINQ 操作默认不可变:它们不改变输入源,只构建新的枚举器。你看到 list.Prepend(x) 时,list 本身还是原来那个 List,长度、内容全没动。
容易踩的坑是忘记赋值或转换:
- ❌
numbers.Prepend(0);→ 结果被丢弃,numbers不变 - ✅
var newNumbers = numbers.Prepend(0).ToList();→ 得到新列表 - ✅
foreach (var n in numbers.Prepend(-1)) { ... }→ 直接遍历新序列,不建中间集合
性能上,Prepend 开销极小(只是包装一层迭代器),但 .ToList() 会分配新数组并复制全部元素;Append 同理,内部也是延迟构造,真正遍历时才“看到”末尾那个值。
和 List.Insert(0, x) / List.Add(x) 的关键区别
这是最常混淆的地方:Prepend/Append 是 LINQ 的“视图级”拼接,而 List.Insert 和 Add 是“原地修改”。它们解决的问题根本不同。
-
List→ 修改原列表,时间复杂度 O(n),因为要搬移所有后续元素.Insert(0, x) -
List→ 原地追加,均摊 O(1).Add(x) -
Prepend(x)→ 不动原数据,返回新IEnumerable,遍历时第一个 yield 是x,再 yield 原序列,O(1) 构建,O(1) 首次访问 -
Append(x)→ 同理,原序列全遍历完后才 yieldx
如果你只是临时需要“看起来带头/带尾”去传给另一个方法(比如 SomeMethod(IEnumerable),直接 Prepend 就够了,别白费劲转 List。
空集合或 null 输入时的行为
Prepend 和 Append 都要求第一个参数(即 this IEnumerable)不能为 null,否则抛 ArgumentNullException。但空集合(如 new int[0].AsEnumerable())完全合法:
-
Enumerable.Empty→ 返回只含().Prepend(99) 99的序列 -
Enumerable.Empty→ 同样只含().Append(99) 99 -
((IEnumerable→ 立刻崩,报错)null).Prepend(1) Value cannot be null. (Parameter 'source')
所以实际用时,如果上游数据可能为 null,得先判空再调用,或者用空合并:(data ?? Enumerable.Empty。
真正容易被忽略的是:这两个方法对“无限序列”也成立(比如 Enumerable.Range(0, int.MaxValue)),但 Append 在这种情况下永远无法到达末尾——除非你只取前 N 项,否则遍历会卡死在最后那个元素上。










