在某些涉及不可变对象的情况下,可能会出现许多语义相同的不同对象。一个简单的例子是将文件中的多行文本读入字符串。从程序的角度来看,两行具有相同的字符序列这一事实是“巧合”,但从程序员的角度来看,可能会出现大量重复。如果许多字符串实例是相同的,则将对这些不同实例的引用更改为对单个实例的引用将节省内存,并且还有助于它们之间的比较(如果两个字符串引用指向同一个字符串,则无需执行字符-逐字符比较以确定它们是否相同)。
对于某些情况,系统提供的字符串实习工具可能很有用。但是,它有几个严重的限制:
- 一旦一个字符串被实习,这个实习副本将永远存在,无论是否存在对它的任何其他引用
- 字符串实习工具仅适用于字符串,不适用于任何其他不可变类型。
如果存在一个 true WeakDictionary<ImmutableClassType, ImmutableClassType>
(对于每个元素,键和值都是相同的),代码可以执行以下操作:
if (theDict.TryGetValue(myString, ref internedString))
myString = internedString;
else
theDict[myString] = myString;
不幸的是,我不知道.net 中有任何内置WeakDictionary<keyType, valType>
类。此外,为每个项目的键和值生成弱引用似乎很浪费,因为这两个引用总是指向同一事物。
我已经阅读了一些关于的内容ConditionalWeakTable
,这听起来像是一个有趣的类,但我认为它在这里不可用,因为目标是能够获取一个实例并找到另一个在语义上等效的独立实例。
对于类的实例将具有明确定义的生命周期的情况,使用常规Dictionary
来合并相同的实例可能是合理的。然而,在许多情况下,可能很难知道什么时候Dictionary
应该放弃这样的物品或清理其中的物品。基于实习的WeakReference
集合将避免此类问题。这样的事情是否存在,或者它是否可以很容易地实施?
附录
正如 svick 所指出的,aDictionary<WeakReference, WeakReference>
会有些问题,因为没有实用的方法来定义IEqualityComparer
一个活WeakReference
的返回GetHashCode
其目标的值,而死的继续返回该值。可以定义一个结构,该结构将包含一个整数目标哈希码值(在构造函数中设置),并且其自己的GetHashCode
将返回该整数。一个轻微的改进可能是使用 aConditionalWeakTable
将 的目标链接WeakReference
到可用于将表项排入队列以进行删除的可终结对象。
我不确定在急切地清理字典与采取更被动的方法(例如,如果自上次扫描以来至少有一个 GC,则在添加项目时执行扫描,以及数量自上次扫描以来添加的项目数超过了幸存的项目数)。浏览字典中的所有内容不会是免费的,但 ConditionalWeakTable 可能也不会是免费的。
PPS
我正在考虑的另一个概念,但我认为它可能不如弱实习方法有用,它是让一个逻辑上不可变的类型拥有一个可变的“时间戳”值,并有一个接受的比较方法它的论点由ref
. 如果发现两个不同的实例相等,则将检查它们的时间戳值。如果两者都为零,则将从全局计数器(-1、-2、-3 等)为它们分配连续的负数。具有(或分配)较低时间戳值的参数将被另一个替换。
使用这种方法,如果许多对象实例被重复地相互比较,许多引用将被替换为对“旧”实例的引用。根据使用模式,这可能会导致大多数相同的对象实例在不使用任何类型的实习字典的情况下被合并。然而,对嵌套对象应用这种方法将需要“不可变”对象允许嵌套对象引用被变异以指向其他假定相同的嵌套对象。如果“假定相同”的对象总是存在,这应该没问题,但如果不是,可能会导致相当奇怪的不当行为。