我最近ConditionalWeakTable<TKey,TValue>在我的搜索中遇到了一个IDictionary使用弱引用的类,正如这里和这里的答案中所建议的那样.
有一篇明确的MSDN文章介绍了该类,并指出:
您可以在System.Runtime.CompilerServices命名空间中找到类.... 它在CompilerServices中,因为它不是通用字典类型:我们打算只让编译器编写者使用它.
然后再说:
...有条件的弱表不是一个通用的集合...但是如果你正在编写自己的.NET语言并且需要公开将属性附加到对象的能力,你一定要查看条件弱表.
与此一致,该类的MSDN条目描述如下:
使编译器能够将对象字段动态附加到托管对象.
显然它最初是为了一个非常特定的目的而创建的 - 帮助DLR,System.Runtime.CompilerServices命名空间体现了这一点.但它似乎发现了更广泛的用途 - 即使在CLR中也是如此.例如,如果我在ILSpy中搜索ConditionalWeakTable的引用,我可以看到它在MEF类和内部WPF 类中使用.CatalogExportProviderDataGridHelper
我的问题是在编译器编写和语言工具之外是否可以使用ConditionalWeakTable,以及在未来的.NET版本中是否会产生额外开销或实现变化的风险.(或者应该避免使用像这样的自定义实现).
另外也进一步阅读这里,这里和这里有关如何ConditionalWeakTable利用一个隐藏的CLR实现的ephemerons(通过System.Runtime.Compiler.Services. DependentHandle)来处理键和值之间的周期问题,以及如何能够不容易在一个自定义的方式来完成.
我在哪里可以找到IDictionary使用弱引用的良好实现?
字典应该只保留对值的弱引用,并最终清除死引用本身.
或者我应该自己写吗?
是否有一个C#类提供弱键或/和弱值的映射?或至少WeakHashMap之类的功能.
在涉及不可变对象的某些情况下,许多不同的对象可能存在,这些对象在语义上是相同的.一个简单的例子是从文件中读取许多行文本到字符串.从程序的角度来看,两行具有相同字符序列的事实将是"巧合",但从程序员的角度来看,可能会出现大量重复.如果许多字符串实例相同,将对这些不同实例的引用更改为对单个实例的引用将节省内存,并且还将促进它们之间的比较(如果两个字符串引用指向相同的字符串,则不需要执行字符 - 通过字符比较来确定它们是相同的).
对于某些情况,系统提供的字符串实习设施可能很有用.但是,它有一些严重的限制:
如果存在true WeakDictionary<ImmutableClassType, ImmutableClassType>(对于每个元素,键和值将相同),代码可以执行以下操作:
if (theDict.TryGetValue(myString, ref internedString))
myString = internedString;
else
theDict[myString] = myString;
Run Code Online (Sandbox Code Playgroud)
不幸的是,我不知道WeakDictionary<keyType, valType>.net中的任何内置类.此外,当两个引用总是指向同一个东西时,为每个项的键和值生成弱引用似乎是浪费的.
我已经阅读了一些关于ConditionalWeakTable,这听起来像一个有趣的类,但我不认为它在这里可用,因为目标是能够采用一个实例并找到另一个在语义上等效的独立实例.
对于类的实例具有明确定义的生命周期的情况,使用常规Dictionary来合并相同的实例可能是合理的.然而,在许多情况下,可能很难知道何时Dictionary应放弃这样的物品或清除其中的物品.WeakReference基于A 的实习收集将避免此类问题.这样的事情是存在的,还是可以很容易实现的?
附录
正如svick所指出的那样,Dictionary<WeakReference, WeakReference>问题会有些问题,因为没有切实可行的方法来定义IEqualityComparer哪一个会实时WeakReference返回GetHashCode其目标的值,并且有一个死人继续返回该值.可以定义一个结构,它将包含一个整数target-hashcode值(在构造函数中设置),并且它自己GetHashCode将返回该整数.稍微改进可能是使用a ConditionalWeakTable将目标链接WeakReference到可终结对象,该目标可用于将表项排入队列以进行删除.
我不确定尝试急切地清理字典与采取更为被动的方法之间的适当平衡是什么(例如,如果自上次扫描以来至少有一个GC,则在添加项目时执行扫描,以及数字自上次扫描以来添加的项目超过了幸存的项目数量).扫描字典中的所有内容并不是免费的,但ConditionalWeakTable可能也不会是免费的.
PPS
我想到的另一个概念,但我认为它可能不如弱实用方法那样有用,就是让一个逻辑不可变类型保存一个可变的"timestamp"值,并且有一个接受的比较方法它的论点ref.如果发现两个不同的实例相等,则将检查它们的时间戳值.如果两者都为零,则将从全局计数器(-1,-2,-3等)中为它们分配连续的负数.具有(或被分配)较低时间戳值的参数然后将被另一个替换.
使用这种方法,如果许多对象实例被重复地相互比较,则许多引用将被替换为对"较旧"实例的引用.根据使用模式,这可能导致大多数相同的对象实例被合并而不使用任何类型的实习字典.但是,对嵌套对象应用这种方法需要"不可变"对象允许嵌套对象引用变异以指向其他所谓相同的嵌套对象.如果"假设相同"的对象总是如此,那么这应该没问题,但如果不是这样,可能会导致相当奇怪的错误行为.