跳转到主内容
新派编程网 - 新潮编程理念,全新技术课堂!

C#中集合字面量新特性_C#Collection Literal教程【推荐】

C# 13集合表达式是替代传统初始化的主力机制,优先用于静态、尺寸确定场景;编译器依目标类型生成最优实现(如ImmutableArray或数组),支持嵌套、展开(..)及编译期常量折叠,但不支持ICollection等可变接口。

集合字面量(Collection Expressions)在 C# 12 中正式落地,C# 13 进一步强化了嵌套、展开和不可变语义。它不是语法糖的“锦上添花”,而是替代
new int[] { }
、
new List { }
和
Enumerable.Range().ToArray()
的主力初始化机制——只要目标类型明确且支持集合初始化,就该优先用
[...]
。 什么时候该用
[1, 2, 3]
而不是
new int[] { 1, 2, 3 }
当你要构造一个**静态、一次性、尺寸确定**的集合时,
[1, 2, 3]
是默认选择。编译器会直接生成紧凑的 IL(比如
ldc.i4.3
+
newarr
),不经过任何中间对象或虚方法调用。 赋值给
int[]
、
string[]
、
Span
、
IReadOnlyList
等接口时,行为由目标类型决定:写成
IReadOnlyList x = [1, 2, 3];
实际生成的是
ImmutableArray
,而非托管数组 写
var x = [1, 2, 3];
时,
x
类型固定为
int[]
,不会退化为
object[]
或引发装箱 旧写法
new int[] { 1, 2, 3 }
在调试器中显示为“数组”,而
[1, 2, 3]
在 IDE 悬停时直接显示元素和长度,更利于快速验证 别在循环体内反复写
[i, i+1, i+2]
——每次都会分配新数组;这种动态场景仍该用
List.Add()
或预分配数组
..
展开运算符实际怎么工作的
..
不是运行时反射或 LINQ 遍历,而是编译器在 AST 阶段识别后,生成针对具体类型的高效拷贝逻辑。例如
[..a, ..b]
赋值给
int[]
,会被重写为
Array.Copy
序列;若目标是
Span
,则用
Span.CopyTo
。 C知道 CSDN推出的一款AI技术问答工具 下载 被展开的对象必须实现
IEnumerable
,且编译器能推导出
T
;
HashSet
虽然可枚举,但不支持集合表达式直接展开(会报错 CS8955)
..
可多次出现:
[x, ..list, y, ..span, z]
合法,顺序严格按书写顺序拼接 不能对
null
展开:
[..possiblyNullList]
编译通过,但运行时抛
NullReferenceException
;需显式判空或用空合并:
[..possiblyNullList ?? []]
嵌套展开不自动扁平化:
var jagged = [[1, 2], [3, 4]];
类型是
int[][]
,不是
int[]
;要扁平化得写
[..jagged[0], ..jagged[1]]
为什么
IReadOnlyCollection
推荐但
ICollection
不行 集合表达式设计哲学明确倾向不可变/只读契约。
IReadOnlyCollection
、
IReadOnlyList
、
IEnumerable
都被原生支持,而
ICollection
和
IList
不在隐式转换列表中——因为它们带
Add
、
Remove
等可变方法,与字面量“声明即完成”的语义冲突。 写
ICollection c = [1, 2, 3];
会编译失败(CS0029);必须显式构造:
new List { 1, 2, 3 }
List
被支持,是因为它实现了集合初始化器(
Add
方法),但底层仍是先分配数组再逐个
Add
,性能不如直接生成数组;仅当后续需修改时才选它 若你定义自定义集合类型,要让它支持
[...]
,必须公开无参构造函数 +
Add(T)
方法,或实现
ISpanFormattable
等特定接口(C# 13 新增) 最易被忽略的一点:集合表达式中的元素表达式会在**编译期求值**(如常量折叠),但变量引用和方法调用(如
[1, ..GetItems(), 2]
)仍为运行时行为。别假设整个
[...]
块是“编译时常量”——只有纯字面量部分才是。

相关文章