.NET不可变集合已经正式发布

2013 年 9 月 30 日

微软基础类库(Base Class Library)团队已经完成了.NET 不可变集合的正式版本,但不包括 ImmutableArray。与其一起发布的还包括针对其它不可变对象类型的设计指南。

如果你需要在多个线程中安全地共享集合,并且允许每个线程在需要时对其内容进行改变。这种场景就是不可变集合所设计的初衷。只读集合在使用时需要复制集合中的全部内容,而新的不可变集合可以以一种更高性能的方式从一个现有集合中进行创建。

使用不可变集合需要特别当心,因为你很容易错误地写成“list.Add(item)”,而正确的方法是“list = list.Add(item)”。甚至编译器也可能产生类似的错误,这也是为什么不可变集合不支持构造函数的原因。考虑以下代码:

复制代码
list = new ImmutableList<int> {1, 2, 3};

在编译后会产生以下代码:

复制代码
temp = new ImmutableList<int>(); temp.Add(1); temp.Add(2) temp.Add(3) list = temp;</int>

由于 3 次 Add 方法的结果都被丢弃,最终整个集合包含的项数目为 0,而不是期望中的 3。

不可变对象指南

Immo Lendwerth 建议,当你在创建自己的不可变对象时,在其中加入适当的 WithXxx 方法。对简单的对象来说,为每一个属性创建一个 WithXxx 方法即可。当属性值需要变化时,该方法会返回当前对象的一个拷贝。

如果某属性代表了一个结合,那么这种模式就需要一点变化。以下这段代码来自 Immo 的发布声明

复制代码
class Order
{
public Order(IEnumerable<OrderLine> lines)
{
Lines = lines.ToImmutableList();
}
public ImmutableList<OrderLine> Lines { get; private set; }
public Order WithLines(IEnumerableOrderLine> value)
{
return Object.ReferenceEquals(Lines, value)
? this
: new Order(value);
}
}

如你所见,WithLines 方法可接受任意 IEnumerable。因此你可以传递一个新创建的 ImmutableList 对象,或者是某个 LINQ 表达式的结果。这种方式已经足以满足需求了,不过他还建议提供某些辅助方法:

复制代码
class Order
{
//...
public Order AddLine(OrderLine value)
{
return WithLines(Lines.Add(value));
}
public Order RemoveLine(OrderLine value)
{
return WithLines(Lines.Remove(value));
}
public Order ReplaceLine(OrderLine oldValue, OrderLine newValue)
{
return oldValue == newValue
? this
: WithLines(Lines.Replace(oldValue, newValue));
}
}

ImmutableArray 被移除

由于性能方面的原因,ImmutableArray 从最终的发布版本中被移除。其原因是:为了满足内存性能指标,ImmutableArray 必须设计成一个值对象,并且为了保持值对象的语义,ImmutableArray 的默认实例必须表现为一个空数组形式。不幸的是,为了达到这一点,对空值的检测(null check)会使得 C#无法移除对数组边界的检测,而这一点是为达到良好 CPU 性能的一个重要考虑事项。

由于 ImmutableArray 类对于 Roslyn 编译器项目非常重要,设计者曾考虑删除会导致性能问题的空值检测功能,但又因此产生了另外的问题, Immo 这样写道

由于所有的值类型都有一个自动产生的默认构造函数,它会将该值类型初始化为它的默认状态,而 ImmutableArray的默认值是空,它的底层数组实现则为 null。因此,AddRange 方法的实现会因为 NullReferenceException 的产生而崩溃。

这一问题还表现在其它一些地方,由于 ImmutableArray实现了某些集合接口(例如 IEnumerable 和 IReadOnlyList),因此你可以把它传递给某些接受这种接口的方法。由于这种接口引用是非空的,使用者在调用它的方法或者属性时不会考虑到有可能产生 NullReferenceException。

基础类库团队并未放弃这个项目,他们还在研究其它设计方式,以争取让 ImmutableArray 重新亮相。

查看英文原文:.NET Immutable Collections Ready for Production

2013 年 9 月 30 日 05:451368
用户头像

发布了 428 篇内容, 共 148.0 次阅读, 收获喜欢 20 次。

关注

评论

发布
暂无评论
发现更多内容

Web3极客日报#127

谢锐 | Frozen

区块链 开源 技术社区 Rebase Web3 Daily

JAVA主流锁

颇风

Java 多线程

重新强调完成的定义

Bob Jiang

Scrum 完成的定义 DoD definition of done

linux文件系统-inode学习整理

戈坞昂

Linux inode

游戏夜读 | Two Sum问题的八个解

game1night

中小型城市商业银行数字化转型实践(三)数据中台建设思路和路径

泡菜小仙

数据中台 数字化转型 数据架构

Web3极客日报 #128

谢锐 | Frozen

区块链 开源 技术社区 Rebase Web3 Daily

Spring Security 中的授权操作原来这么简单

江南一点雨

Java spring Spring Boot spring security

给苹果提醒APP配个助手

BabyKing

提醒助手 TODO 奇妙清单 Reminders Helper

换脸新潮流:BIGO风靡全球的人脸风格迁移技术

DT极客

中小型城市商业银行数字化转型实践(一)整体技术架构转型(双态IT)

泡菜小仙

数字化转型 架构设计 技术架构

在Gitlab-ce的Docker中使用自定义端口

天飞

Docker gitlab

Redis缓存三大问题

Bruce Duan

redis 缓存穿透 缓存击穿 缓存雪崩

Kafka系列第7篇:你必须要知道集群内部工作原理的一些事!

z小赵

大数据 kafka 实时计算

ZooKeeper,到底如何选主?

奈学教育

识别代码中的坏味道(三)

Page

敏捷开发 面向对象 重构 代码质量 代码坏味道

东哥和刘亦菲的故事

张利东

R

Deno 入门手册:附大量 TypeScript 代码实例

寇云

node.js typescript

谈谈控制感(7):底线思维与控制感

史方远

职场 心理 成长

如何更自信的写作

七镜花园-董一凡

写作

Vue+SpringBoot+SpreadJS 实现的在线文档

Geek_Willie

Spring Boot Vue SpreadJS

产品周刊 | 第 15 期(20200517)

Herbert

产品 设计 产品经理 产品设计

单核小鸡上的Minikube实践(一)

摩登土狗

Docker Linux DevOps k8s minikube

项目提升服务过程与总结稿

Geek_bc0aff

npm下载electron缓慢的问题

玏佾

npm Electron

设计模式前传——为什么要学设计模式

海星

Java 面试 设计模式

MacOS 下使用VSCode进行GoLang Test报错

北纬32°

golang macos vscode Unit Test debug

DDD 实践手册(番外篇: 事件风暴-概念)

Joshua

领域驱动设计 DDD 事件风暴 事件驱动 Event Storming

NIO看破也说破(四)—— Java的NIO

小眼睛聊技术

Java 学习 开源 架构 后端

中小型城市商业银行数字化转型实践(二)集成关系ESB APIGateway ServiceMesh

泡菜小仙

架构设计 集成架构 ESB

我的读书笔记-樊登读书法

lmymirror

学习 读书笔记 方法论 读书方式

.NET不可变集合已经正式发布-InfoQ