缓存反射数据

Cod*_*aos 49 .net c# garbage-collection .net-assembly

缓存从反射获得的昂贵数据的最佳方法是什么?例如,大多数快速序列化程序都会缓存此类信息,因此无需在每次遇到相同类型时进行反映.他们甚至可以生成一个动态方法,从类型中查找.

之前.net 4

传统上我使用了普通的静态字典.例如:

private static ConcurrentDictionary<Type, Action<object>> cache;

public static DoSomething(object o)
{
    Action<object> action;
    if(cache.TryGetValue(o.GetType(), out action)) //Simple lookup, fast!
    {
        action(o);
    }
    else
    {
        // Do reflection to get the action
        // slow
    }
} 
Run Code Online (Sandbox Code Playgroud)

这会泄漏一点内存,但是因为它只对每种类型和类型执行一次,只要AppDomain我不认为这是一个问题.

自.net 4

但是现在.net 4引入了用于动态类型生成的可收集组件.如果我曾经使用过可DoSomething收集组件中声明的对象,那么组件将不会被卸载.哎哟.

那么在.net 4中缓存每种类型信息的最佳方法是什么?没有遇到这个问题?我能想到的最简单的解决方案是:

private static ConcurrentDictionary<WeakReference, TCachedData> cache.
Run Code Online (Sandbox Code Playgroud)

IEqualityComparer<T>我必须使用它会表现得非常奇怪,也可能违反合同.我不确定查找的速度有多快.

另一个想法是使用到期超时.可能是最简单的解决方案,但感觉有点不雅.


在类型作为通用参数提供的情况下,我可以使用嵌套的泛型类,它不应该遇到这个问题.但是如果在变量中提供类型,则它不起作用.

class MyReflection
{
    internal Cache<T>
    {
        internal static TData data;
    }

    void DoSomething<T>()
    {
        DoSomethingWithData(Cache<T>.data);
        //Obviously simplified, should have similar creation logic to the previous code.
    }
}
Run Code Online (Sandbox Code Playgroud)

更新:我刚才有一个想法是使用Type.AssemblyQualifiedName关键.这应该唯一地标识该类型而不将其保留在内存中.我甚至可以在这个字符串上使用引用标识.

此解决方案仍然存在的一个问题是缓存的值也可能保留对类型的引用.如果我使用弱引用,它很可能会在程序集卸载之前到期.而且我不确定从弱参考中获取正常参考是多么便宜.看起来我需要做一些测试和基准测试.

Iva*_*lov 35

ConcurrentDictionary<WeakReference, CachedData>在这种情况下是不正确的.假设我们正在尝试缓存类型T的信息,所以WeakReference.Target==typeof(T).CachedData最有可能也包含引用typeof(T).由于您ConcurrentDictionary<TKey, TValue>内部集合中的商店项目Node<TKey, TValue>将具有强引用链:ConcurrentDictionaryinstance - > Nodeinstance - > Valueproperty(CachedDatainstance) - > typeof(T).通常,在值可以引用其键的情况下,使用WeakReference无法避免内存泄漏.

有必要添加对ephemerons的支持,以使这种情况成为可能,而不会造成内存泄漏.幸运的是.NET 4.0支持它们,我们有ConditionalWeakTable<TKey, TValue>类.似乎引入它的原因接近你的任务.

此方法还解决了更新中提到的问题,因为只要加载程序集,Type的引用就会生效.