自 C# 13 起(需配合 .NET 9),语言新增了展开运算符(..),它让集合操作变得像写自然语言一样直白:你想“把这几个东西放一起”,就真的能直接“放一起”。
在 C# 中拼接数组、合并列表、复制集合,过去常依赖 Array.Copy()、.Concat() 或 .AddRange()。这些方法固然可靠,但代码往往冗长、嵌套深、意图不直观——尤其当需要组合多个来源时,几行逻辑可能写出十几行样板代码。
好消息是,从C# 13 起(需配合 .NET 9),语言新增了展开运算符(..),它让集合操作变得像写自然语言一样直白:你想“把这几个东西放一起”,就真的能直接“放一起”。

展开运算符怎么用?看几个典型场景
最基本的用法是合并两个数组或列表:
int[] part1 = [1, 2, 3];
int[] part2 = [4, 5, 6];
int[] combined = [..part1, ..part2]; // 结果:[1, 2, 3, 4, 5, 6]
不需要 .Concat(),不用 ToArray(),一行搞定,结果还是强类型数组。
也可以混合单个元素和集合:
int[] extras = [10, 20];
int[] allItems = [0, ..extras, 30]; // [0, 10, 20, 30]
甚至支持不同类型的集合混用——只要元素类型兼容:
int[] arr = [1, 2];
List list = [3, 4];
Span span = stackalloc int[] { 5, 6 };
var result = [..arr, ..list, ..span]; // [1, 2, 3, 4, 5, 6]
实际项目中,它特别适合构建动态配置:
string[] basePaths = ["/api", "/health"];
string[] customPaths = GetCustomEndpoints();
string[] allPaths = ["/", ..basePaths, ..customPaths];
或组装 API 响应:
var commonHeaders = ["Cache-Control: no-store"];
var customHeaders = GetServiceHeaders();
var responseHeaders = [..commonHeaders, ..customHeaders, "X-Request-ID: abc123"];
创建集合副本也更简洁:
int[] original = [1, 2, 3];
int[] copy = [..original]; // 等同于 original.ToArray(),但更短
为什么推荐它?
可读性高:[a, ..rest, b] 直观表达“开头是 a,中间是 rest 全部,结尾是 b”,意图一目了然。
减少样板代码:告别 new List
类型安全:编译时检查元素类型,避免运行时类型不匹配。
性能合理:内部优化为单次内存分配,比链式 .Concat() 更高效(尤其多段拼接时)。
兼容性强:支持任何 IEnumerable
使用时要注意什么?
展开运算符创建的是浅拷贝——和 ToList()、ToArray() 行为一致,对引用类型元素,副本与原集合共享对象实例。
它不适用于需要转换或过滤的场景。例如,若想把所有 ID 加 100 后合并,仍需用 LINQ:
// ✅ 正确:需要转换时用 Select
var shifted = [..part1.Select(x => x + 100), ..part2];
// ❌ 低效:用 Spread 再手动改,反而更啰嗦
另外,目前仅支持 C# 13 配合 .NET 9。若项目仍在 .NET 6/8 或 C# 12,仍需沿用传统方式。可通过项目文件中的
何时该用、何时不用?
推荐使用场景:合并多个同类型集合;快速创建副本;动态组装配置、路径、参数列表;构造包含固定头尾和中间动态内容的集合。
暂不推荐场景:需要对元素做映射、过滤、排序等变换;处理超大集合(大于100万项),且对内存分配有极致要求;项目尚未升级到 .NET 9 / C# 13。
结语
展开运算符不是颠覆性创新,而是 C# 向“少写代码、多表意图”迈出的扎实一步。它不增加新概念,只是把开发者早已习惯的“展开”思维——如 JavaScript 的 ...、Python 的 *——自然地融入 C# 语法。
当一段集合操作需要反复读三遍才能看懂时,不妨试试 ..。往往一行简洁代码,胜过十行“正确但繁琐”的旧式写法。毕竟,好代码不仅要能跑,更要让人一眼看懂——这正是现代 C# 持续进化的核心目标。
