写点什么

C#的未来:不可变类

2015 年 6 月 07 日

本文是 C#的未来系列文章的最后一篇了,这次我们将讨论第 159 号提案,它建议在编译器中加入对不可变类的支持。虽说在 C#中创建不可变类型一直以来都是可以做到的,并且 C# 6 还将进一步简化这一过程,但目前还没有一种方式能够“将类声明为不可变”,并让编译器对这一声明进行校验。

这一提议看起来似乎并非十分重要,因为对类进行手动检查也不是非常困难的事。但如果缺少了对于不可变性的某种声明,就难以了解开发者的意图。应用程序的开发者可能会做出某些假设,例如可以在多线程环境中安全地使用某个类,却发现在下一个版本中,类库的开发者为其加入了某个属性的 setter 方法,或是其它任何一种可变的、非线程安全的特性。

第 159 号提议推荐通过使用一个“immutable”关键字或者属性,显式地表明某个类型在任何情况下都不能更改。此外,一个不可变对象只能够引用其它不可变对象。

不可变对象的构造函数也具有一些限制,尤其是它们不能够通过“this”变量调用方法,因为这有可能将该对象在构造过程完成之前“泄漏”出去,从而破坏了对不可变性的承诺。至于这种泄漏应当引起一个错误还是一次警告,这一点可以再议。Sam Harwell 写道:

我比较倾向于发出警告的做法,虽然这种做法并不常见,并且也不是推荐的编码方式,但也很难肯定地说绝对没有人需要编写这样的代码。

不可变性与 Pure

第一眼看上去,不可变性与 Pure(纯对象或方法)约束的作用似乎是相同的,但它们之间确实存在着一些重要的不同之处。

  • 一个纯对象可以引用其它并未标注为 Pure 的对象,而正如之前所说,不可变类型不允许引用可变类型。
  • 纯方法不允许进行任何可见的状态更改,包括对当前对象与任何参数的更改。
  • 纯方法或纯属性可以更改内部状态。举例来说,它可以将某个计算的结果进行缓存以便重用。这一点并不违反纯方法的承诺,因为外部的观察者不会察觉到内部的变化。而不可变对象则没有这种能力。
  • 不可变对象中的方法只保证不会更改对象的状态,但允许对输入参数的状态进行更改。

考虑到这些不同之处的存在,相信你会看到许多同时具有不可变性与 Pure 特性的对象。

泛型与不可变性

泛型类型也将支持不可变性。要实现这一点,通常需要为每个类型参数都设定一个不可变性的限制。但这一条规则还有待商榷,有人认为可以直接从类型的参数中推断出不可变性,而无需显式地将其列为不可变。

欺骗

在某些情况下,你必须对类型系统进行欺骗,这一点已经得到了认可。打个比方,ImmutableArray 是对一个普通数组的封装。而如果按照这条提议的基本原则来说,这种行为是不允许出现的。为了处理这种场景,你可以在这个类的标注中进行声明,表示你是有意地违背了这条原则,并且已经仔细地考虑过这样做的后果。这种做法与“unsafe”关键字的行为很相似,因为后者也是为了这种场合才出现的。

由于“unsafe”关键字的意思已经固定了,因此该提议考虑使用其它的关键字。目前来说,得到最多认可的关键字是“mutable”,不过看起来大家对此都不是十分满意。

只读性 —— 隐式或显式

在一个不可变对象中,每个字段在语义上都是只读的。某些开发者认为不必显式地进行声明,这可以减少代码的冗长度。另一些人则认为,正如在静态类中声明静态字段一样,每个字段都应该显式地标注为只读。

内部或外部校验

具体在何处强制不可变性,这一点需要认真考虑。某些人认为,正如代码契约与 Pure 属性一样,不可变性也应当由某个外部分析器进行处理。这样一来,开发团队就能够自行决定是否需要以编程方式强制不可变性。

另一部分开发者则认为,正因为如此,不可变性更不应当由外部工具进行处理。他们希望编译器能够进行不可变性的检查,这样就不会在无意中破坏了它的不可变性。

来自于微软的 Jared Parsons 写道:

我认为在这种场合使用分析器是错误的方案,在一个单一的 C#项目中,要强制一系列规则的应用,甚至是对某种 C#的变体进行分析,分析器都是一种优秀的选择。因为我对编译过程具有掌控权,因此可以随意地选择并使用分析器。

而如果需要在多个项目中强制某些规则,尤其是在这些项目属于不同开发者的情况下,分析器的作用就降低了。没有什么机制能够强制对某个项目引用通过某种分析器进行扫描,这种情况下唯一有效的强制措施就是双方的携手合作了。

这种情况与 Pure 属性的承诺不同,后者只是表示在某个对象中的任何方法或是属性都不会“产生任何可见的状态更改”。

查看英文原文: C# Futures: Immutable Classes


感谢张龙对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们,并与我们的编辑和其他读者朋友交流(欢迎加入 InfoQ 读者交流群)。

2015 年 6 月 07 日 05:552281
用户头像

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

关注

评论

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

架构师训练营 - 第一周学习总结

牛牛

学习 极客大学架构师训练营

一周信创舆情观察(6.1~6.7)

统小信uos

大数据 网络安全 新基建

第一周作业:食堂就餐卡系统设计

尔东雨田

极客大学架构师训练营

【总结】第一周架构师如何做架构

chengjing

第一周学习总结

小海豚

学习

被迫重构代码,这次我干掉了 if-else

程序员内点事

第一周作业

Geek_2b3614

UML 体验(就餐卡系统设计)

陈皮

食堂就餐卡系统架构设计文档

小叶

架构设计

一味的坚持,或许只是徒劳

这小胖猫

逻辑思维 职业成长 工作体会

4天如何完爆Kafka源码核心流程!

奈学教育

kafka

架构师训练营-作业-1】食堂就餐卡系统设计

superman

学习 极客大学架构师训练营

极客时间架构课Week01-作业二:学习总结

yulyulcl

架构师训练营第一周总结

皓首不倦

架构师是什么?

芥末

极客大学架构师训练营

架构师训练营第一周作业 - 食堂就餐卡系统设计

阿德

架构设计文档

talen

技术管理者标准管理模板

飒然Hang

架构师训练营第01周——总结

李伟

极客大学架构师训练营

第一周学习总结

AspYc

架构师训练营第 1 周 _ 食堂就餐卡系统设计

方舟勇士

课程练习

4天如何完爆Kafka源码核心流程!

古月木易

kafka

如何成为一个架构师?

逍遥乐天

极客大学架构师训练营

食堂就餐卡系统设计

架构设计 极客大学架构师训练营

第一周作业:食堂就餐卡系统设计

晓雷

你还在为 TCP 重传、滑动窗口、流量控制、拥塞控制发愁吗?看完图解就不愁了

小林coding

TCP 计算机网络 网络协议

作业1

annie

极客大学架构师训练营

第一周命题作业

AspYc

写作的几点建议:面对卡文,写别人的题目,栩栩如生的写作

七镜花园-董一凡

写作

第一周作业

东哥

极客大学架构师训练营

架构师入门之架构方法

知识乞丐

极客大学架构师训练营

演讲经验交流会|ArchSummit 上海站

演讲经验交流会|ArchSummit 上海站

C#的未来:不可变类-InfoQ