缓存反射数据

问题描述 投票:49回答:3

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

。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
    }
} 

这会泄漏一些内存,但是由于它只对每个Type执行一次,并且只要AppDomain存在,Type就不会存在问题。

自.net 4起

但是现在.net 4引入了Collectible Assemblies for Dynamic Type Generation。如果我曾经在可收集程序集中声明的对象上使用过DoSomething,则该程序集将永远不会被卸载。哎哟

因此,在不受此问题困扰的.net 4中缓存每种类型信息的最佳方法是什么?我能想到的最简单的解决方案是:

private static ConcurrentDictionary<WeakReference, TCachedData> cache.

但是我必须使用的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.
    }
}

Update

:我刚刚想到的一个想法是使用Type.AssemblyQualifiedName作为键。那应该唯一地标识该类型而不将其保留在内存中。我什至可能不会在此字符串上使用引用身份。

此解决方案仍然存在的一个问题是,缓存的值可能还会保留对该类型的引用。而且,如果我为此使用弱引用,则它很可能在卸载程序集之前就过期了。我不确定从弱引用中获取普通引用有多便宜。看来我需要做一些测试和基准测试。

缓存从反射获得的昂贵数据的最佳方法是什么?例如,大多数快速的序列化程序会缓存此类信息,因此无需在每次遇到相同类型时都进行反映...

c# .net garbage-collection .net-assembly
3个回答
35
投票

ConcurrentDictionary<WeakReference, CachedData>在这种情况下不正确。假设我们正在尝试缓存类型T的信息,因此WeakReference.Target==typeof(T)。 CachedData最有可能还将包含typeof(T)的引用。当ConcurrentDictionary<TKey, TValue>将项目存储在Node<TKey, TValue>的内部集合中时,您将具有强引用链:ConcurrentDictionary实例-> Node实例-> Value属性(CachedData实例)-> typeof(T)。通常,在Value可以引用其Key的情况下,使用WeakReference避免内存泄漏是不可能的。


1
投票

您应该签出reasons


-1
投票

我可能会在这里说明显而易见的内容,但是:

© www.soinside.com 2019 - 2024. All rights reserved.