0

如果对象是不可变的,那么制作对象的深层副本非常容易且高效——只需复制该对象的内存指针即可。

进行深度相等检查也非常简单有效 - 只需比较指针即可。

但是如果数据来自外部世界并且我们需要检查它的身份会发生什么?

考虑以下示例:

  • 应用程序从数据库中查询 Post 的数据,将其反序列化为不可变的 Post 对象(模型)并将其缓存在内存中。
  • 一段时间后,应用程序再次查询相同的数据,并将其反序列化为不可变的 Post 对象。
  • 现在,我们如何检查 Post 是否已更改?我们不能只比较不可变对象的引用来检查身份。引用会有所不同(因为我们对数据进行了两次反序列化),但数据本身可能仍然相同。

如何处理这种情况?

4

1 回答 1

1

两种可能可行的方法,或者可能为您提供更多关于未来方法的想法:

  1. 保留一个或多个已知不可变实例的字典,并查找您构造的实例以查看它们是否在字典中;如果是这样,将字典实例替换为新读取的实例。请注意,除非每次构造对象时都使用字典,否则引用 *equality* 可用于加速比较,但引用 *inequality* 不能。然而,让对象缓存它们的哈希码可能很有用,因为不相等的项目*通常*具有不同的哈希值。还要注意,除非您使用某种“WeakDictionary”,否则您必须确保定期清除字典(实际上,这意味着除非您有 WeakDictionary,否则很难清除未使用的项目至少不偶尔清理用过的)。
  2. 可以给每个对象一个创建时间指示器(可能是一个静态的 `Interlocked.Increment` 计数器)以及一个指向已知相等的最早(第一个创建的)对象的链接。比较对象时,请遵循“旧的已知相等”对象的链。如果链到达同一个对象,则原始对象相等。否则,如果对象的哈希码匹配,则测试它们的值是否相等。如果它们相等,请更新较新的链接以指向较旧的链接。如果任一起始对象具有多个链接的链,则 `Interlocked.CompareExchange` 其链接直接指向最早的已知相等对象。使用这种方法,值相等的对象之间的比较将导致它们形成派系;集团内价值相等的对象之间的比较会很快,不同派系中等值对象之间的比较,将两个派系合二为一。另请注意,可能需要让节点的消费者持有包装对象,而不是直接链接到节点本身;如果这样做了,一个集团中所有节点的包装器可以共享对同一内部数据项的引用。

如果有合适的字典类型可用,方法 #1 可能是一个不错的方法,但方法 #2 也可能具有一些相当大的优势。#2最大的烦恼是它为对象访问添加了额外的间接层。尽管如此,能够让对象迅速将自己整合成派系可能是一个主要的优势。

于 2012-11-01T19:01:29.453 回答