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

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


















