跳转到主内容
极星编程网:以代码为星,赴技术山海!

C#中Immutable Collections的用法_C#不可变集合操作教程【进阶】

ImmutableList不可替代List作只读场景:它是结构共享的树形集合,必须按不可变范式使用,否则性能暴跌或逻辑静默失效;其索引访问为O(log n),远慢于ImmutableArray的O(1)。 直接用
ImmutableList
替代
List
做只读场景,大概率出问题;它不是“只读包装”,而是带结构共享、值语义明确的树形集合,必须按不可变范式使用——否则要么性能暴跌,要么逻辑静默失效。 ImmutableList.Create() 和 ImmutableList.Empty 的区别在哪 两者都创建空实例,但语义和开销不同:
ImmutableList.Create()
返回一个含 0 个元素的树节点,有装箱/构造开销,适合后续链式调用(如
.Add(1).Add(2)
)
ImmutableList.Empty
是静态只读单例,无分配、无装箱,适合纯占位或作为 Builder 起点 误用
new List().AsReadOnly().ToImmutableList()
:多一层
ReadOnlyCollection
包装,白跑一次遍历,还引入冗余引用 为什么 Add() 后不接返回值就等于没写 因为所有修改方法(
Add()
、
Remove()
、
SetItem()
)都不改变原实例,只返回新实例:
list.Add(42);
→ 原
list
仍是旧引用,新增元素丢失
var newList = list.Add(42);
→ 正确,
newList
指向新树节点 链式调用如
list.Add(1).Remove(0).Insert(0, 99)
可行,但每步都新建中间对象;100 次循环调用会生成 100 个临时树,内存和 GC 压力陡增 Builder 模式什么时候必须用 批量变更(>10 次增删改)或循环中累积操作时,不用 Builder 就是自找麻烦:
var builder = list.ToBuilder();
开销极小,底层是可变数组,非树结构
builder.Add()
、
builder.RemoveAt()
、
builder[0] = x
全部是 O(1) 操作
builder.ToImmutable()
才真正构建平衡树,仅一次内存分配 已知容量?用
ImmutableList.CreateBuilder(capacity)
预分配,避免 builder 内部数组扩容拷贝 ImmutableList 和 ImmutableArray 哪个更适合只读高频访问 看场景选,不是越“不可变”越好: 需要频繁索引访问(如配置表查值)、且构建后永不修改 → 选
ImmutableArray
:底层是紧凑数组,
array[i]
是纯指针偏移,无虚调用、无装箱、无树跳转 需要后续仍可能小范围增删(如事件队列、状态快照链)→ 选
ImmutableList
:树结构支持高效插入/删除,但每次访问要多跳 1–2 层指针 误把
ImmutableList
当“高性能只读数组”用:访问延迟比
ImmutableArray
高 3–5 倍,尤其在热路径上明显 真正容易被忽略的是:ImmutableList 的“不可变”不等于“零开销”。它的结构共享节省了复制内存,但换来的是间接访问成本。如果你的集合只读、固定、访问密集,优先走
ImmutableArray.CreateBuilder().AddRange(...).ToImmutable()
这条路——这才是 .NET 里最接近“冻结数组”的做法。

相关文章