我应该尽可能让我的课程不可变吗?
我曾经读过Joshua Bloch撰写的“Effective Java”一书,他建议出于各种原因使所有业务对象不可变。 (例如线程安全) 这也适用于C#吗?
您是否尝试使对象不可变,因此在使用它们时遇到的问题较少? 或者不值得为它们创造它们带来的不便?
答案 0 :(得分:23)
不可变的Eric Lippert在这个主题上写了whole series of blog posts。第一部分是here。
从他链接到的earlier post引用:
ASIDE:不可变的数据结构是C#未来的发展方向。如果您知道它永远不会改变,那么对数据结构进行推理要容易得多。由于它们无法修改,因此它们是自动线程安全的。由于它们无法修改,因此您可以维护一堆过去的“快照”结构,并且突然撤消重做实现变得微不足道。在不利方面,他们确实倾向于咀嚼记忆,但是,嘿,这就是垃圾收集的发明,所以不要为此而烦恼。
答案 1 :(得分:4)
这更像是一种意见类型的答案,但是......
我发现理解程序的容易程度,即维护和调试所述应用程序,与在每个组件的处理期间发生的有状态转换的量成反比。我需要在脑子里徘徊的状态越少,我就越关注编写算法时的逻辑。
答案 2 :(得分:1)
不可变对象是函数式编程的核心特征;它有自己的优点和缺点。 (例如,链接列表实际上不可能是不可变的,但是不可变对象使并行性变得轻而易举。)因此,当您对帖子发表评论时,答案是“它取决于”。
答案 3 :(得分:1)
在我的脑海中,我无法想到不可变对象使某些线程安全代码“更好”的原因。
如果我想要一个对象是线程安全的,我会对它进行锁定,或者我会复制它并在我完成它之后更新引用。我通常不希望每一个小变化都有一个新对象。
对我来说,不可变的字符串为线程化创造了比它有帮助的更多麻烦。
我实际上是在使用不安全的代码来制作“就地”“ToUpper”而不是内置的String.ToUpper()。它运行速度大约快4倍,消耗峰值内存的1/2。
答案 4 :(得分:0)
不可变结构的另一个好处是,您可以在本地缓存它们的实例,并在多个线程中重用它们,而不必担心会出现意外行为,如果它们是可变的话。
例如,假设您正在使用外部缓存服务,例如memcached或Velocity或其他一些同样简单的分布式哈希表服务。您可以使用C#客户端库并将其称为足够好。然而,考虑到像Web请求场景这样的短期上下文,这对资源来说是浪费的。你真正想要的是将每个对象从缓存中拉出一次,在你的上下文中只拉一次。
完成此任务的最安全方法是在缓存提供程序前面的进程中放置一个本地哈希表。在第一次请求缓存键时,您需要下拉代表您希望使用的对象的序列化字节流,并将该字节流存储在本地哈希表中。在对相同缓存键的后续请求中,只需在本地哈希表中查找字节流,并将对象反序列化为每个请求的新实例。这是为了防止多次冗余访问缓存服务器节点,以获取假设在上下文的生命周期内未发生变化的相同信息。
使用不可变结构,您可以在第一个请求中仅对字节流进行反序列化一次,并将反序列化的实例存储在哈希表中而不是字节流中,只需共享对象的一个单个不可变实例。这显然减少了反序列化惩罚,如果您的消费代码以这样的方式编写而不关心它对缓存提供程序进行了多少调用,假设缓存比查询底层数据存储更快,则可以相当快地加起来。
也许这更像是一个主观的答案,但这是一个特定的问题,可以通过使用不可变结构来唯一解决,所以我认为它与分享相关。