C# 不可变对象是好的实践吗?

C# 不可变对象是好的实践吗?,c#,.net,immutability,C#,.net,Immutability,我应该尽可能使我的类不可变吗 我曾经读过Joshua Bloch的《有效Java》一书,他建议出于各种原因使所有业务对象都不可变。(例如螺纹安全) 这也适用于C#吗 您是否尝试使对象不可变,以便在使用它们时问题更少? 还是不值得为创建它们而带来不便?不变的埃里克·利珀特(Eric Lippert)就这个话题写了一系列博客文章。第一部分是 引用他链接到的 旁白:不可变的数据结构是C#未来的发展方向。如果知道数据结构永远不会改变,那么对它进行推理就容易多了。由于无法修改它们,因此它们是自动线程安全的

我应该尽可能使我的类不可变吗

我曾经读过Joshua Bloch的《有效Java》一书,他建议出于各种原因使所有业务对象都不可变。(例如螺纹安全) 这也适用于C#吗

您是否尝试使对象不可变,以便在使用它们时问题更少?
还是不值得为创建它们而带来不便?

不变的埃里克·利珀特(Eric Lippert)就这个话题写了一系列博客文章。第一部分是

引用他链接到的

旁白:不可变的数据结构是C#未来的发展方向。如果知道数据结构永远不会改变,那么对它进行推理就容易多了。由于无法修改它们,因此它们是自动线程安全的。由于它们无法修改,您可以维护该结构过去的“快照”堆栈,然后突然间撤销重做实现变得微不足道。另一方面,它们确实会吞噬内存,但嘿,垃圾收集就是为了这个而发明的,所以别担心


这将是一个意见型的答案,但


我发现理解程序的容易程度,即维护和调试所述应用程序,与处理每个组件期间发生的状态转换量成反比。我需要在头脑中反复思考的状态越少,我就越能在编写算法时关注算法中的逻辑。

不可变对象是函数式编程的核心特征;它有自己的优点和缺点。(例如,链表实际上不可能是不可变的,但不可变对象使并行性变得轻而易举。)因此,正如你在文章中的评论所指出的,答案是“视情况而定”。

在我的脑海中,我想不出不可变对象使线程安全代码“更好”的原因

如果我想要一个对象是线程安全的,我会在它周围加上一个锁,或者我会复制它,并在我完成工作后更新引用。我通常不希望每一个小小的改变都有一个新的对象

对我来说,不可变字符串给线程带来的麻烦要多于它的帮助


实际上,我特意使用不安全的代码isntead内置字符串.ToUpper()生成了一个“就地”ToUpper。它的运行速度大约是峰值内存的4倍,消耗了峰值内存的1/2。

不可变结构的另一个好处是,您可以在本地缓存它们的实例,并跨多个线程重用它们,而不必担心意外行为,如果它们是可变的,则会发生这种情况

例如,假设您正在使用一个外部缓存服务,比如memcached或Velocity,或者其他一些同样简单的分布式哈希表服务。您可以使用C#客户机库并称之为足够好的库。然而,如果给定一个像web请求场景这样的短期上下文,那么这是浪费资源的。您真正想要的是在您的上下文中从缓存中提取每个对象一次且仅一次

完成这项工作最安全的方法是在进程中的缓存提供程序前面放置一个本地哈希表。在第一次请求缓存键时,您将下拉表示希望使用的对象的序列化字节流,并将该字节流存储在本地哈希表中。对于相同缓存键的后续请求,只需在本地哈希表中查找字节流,并将对象反序列化为每个请求的新实例。这是为了防止对假定在上下文的生命周期内未更改的相同信息多次重复访问缓存服务器节点

使用不可变结构,您可以在第一次请求时仅对字节流反序列化一次,而不用将反序列化实例存储在哈希表中而不是字节流中,只共享对象的一个不可变实例。这显然减少了反序列化惩罚,如果您的消费代码的编写方式不关心它对缓存提供程序的调用次数(假设缓存比查询底层数据存储快),那么反序列化惩罚的累积速度会相当快


也许这更像是一个主观的答案,但这是一个特定的问题,可以通过使用不可变的结构来唯一地解决,因此我认为这与共享有关。

不幸的是,这个问题的答案是“它取决于”。这取决于什么?有很多因素,其中大部分是你的项目的局部因素,而不是一般性因素。。。不变的个人特质是积极的还是消极的我高度怀疑埃里克是一成不变的。首先,我很确定年龄的变化是可以观察到的。只有当一个人考虑到他的四维自我对当前时间的三维投影时。@Thomas,啊,你是说Eric Lippert?!很高兴你澄清了这一点——我一直盲目地认为你指的是埃里克Lippert@d998af36e8542af4ab858b7e69d54a8480b1ba37:-)“你们都是”应该是“你们都是”,或者更好的形式是“你们都是”。缓存数据的波动性是一个问题,但假设是您的“上下文”或“工作单元”是如此短暂,以至于不断地从缓存节点或底层数据存储重新查询数据是没有意义的。例如,在为单个请求服务的工作单元的短生命周期内,拉动一次就足以保持数据的一致视图。然而,对业务逻辑进行编码以确保每段数据只查询一次是一个很难解决的问题,并且会使逻辑复杂化。要使其不可变,单链表非常简单。另一方面,双链接列表……您在代码周围加了一个锁,以确保其线程安全,但您能保证团队中的每个其他开发人员都会这样做吗?你能保证你