缓存反射数据
本文关键字:数据 反射 缓存 | 更新日期: 2023-09-27 18:01:17
缓存从反射获得的昂贵数据的最佳方法是什么?例如,大多数快速序列化器都会缓存这些信息,这样它们就不需要在每次遇到相同类型时都进行反映。它们甚至可能生成一个动态方法,从类型中查找。
.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
}
}
这会泄漏一些内存,但由于它只对每个类型泄漏一次,并且类型的寿命与AppDomain
一样长,所以我不认为这是一个问题。
从。net 4
但是现在。net 4引入了用于动态类型生成的可收集程序集。如果我在可收集程序集中声明的对象上使用了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.
}
}
更新:我刚刚有一个想法是使用Type.AssemblyQualifiedName
作为关键。它应该唯一地标识该类型,而无需将其保存在内存中。我甚至可以在这个字符串上使用引用恒等式。
这个解决方案仍然存在的一个问题是,缓存的值也可能保留对该类型的引用。如果我为此使用弱引用,它很可能会在程序集卸载之前过期。而且我不确定从弱引用中获得正常引用有多便宜。看来我需要做一些测试和基准。
ConcurrentDictionary<WeakReference, CachedData>
在这种情况下是不正确的。假设我们尝试缓存类型T的信息,所以是WeakReference.Target==typeof(T)
。CachedData很可能也包含typeof(T)
的引用。由于ConcurrentDictionary<TKey, TValue>
将项目存储在Node<TKey, TValue>
的内部集合中,您将拥有强引用链:ConcurrentDictionary
instance -> Node
instance -> Value
property (CachedData
instance) -> typeof(T)
。一般来说,当值可能有对其键的引用时,使用WeakReference是不可能避免内存泄漏的。
有必要添加对ephemerons的支持,以使这种场景在不发生内存泄漏的情况下成为可能。幸运的是。net 4.0支持它们,并且我们有ConditionalWeakTable<TKey, TValue>
类。看来引入它的原因与你的任务很接近。
此方法还解决了更新中提到的问题,因为只要加载Assembly,对Type的引用就会存在。
你应该看看fastreflect库
你可以使用正常反射来动态地生成新的代码&然后发出/编译它,然后缓存编译后的版本。我认为集合的想法是有希望的,避免内存泄漏,而不必从一个单独的应用程序域加载/卸载。但是,内存泄漏应该可以忽略不计,除非您正在编译数百个方法。
这是一篇关于在运行时动态编译代码的博文:http://introspectingcode.blogspot.com/2011/06/dynamically-compile-code-at-runtime.html
下面是我过去用来存储MethodInfo/PropertyInfo对象的类似并发字典方法&它看起来确实更快,但我认为那是在Silverlight的旧版本中。我相信。net有它自己的内部反射缓存,这使得它没有必要。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Collections.Concurrent;
namespace NetSteps.Common.Reflection
{
public static class Reflection
{
private static ConcurrentDictionary<Type, Dictionary<string, PropertyInfo>> reflectionPropertyCache = new ConcurrentDictionary<Type, Dictionary<string, PropertyInfo>>();
public static List<PropertyInfo> FindClassProperties(Type objectType)
{
if (reflectionPropertyCache.ContainsKey(objectType))
return reflectionPropertyCache[objectType].Values.ToList();
var result = objectType.GetProperties().ToDictionary(p => p.Name, p => p);
reflectionPropertyCache.TryAdd(objectType, result);
return result.Values.ToList();
}
}
}
我可能在这里说的很明显,但是:
缓存提供程序通常不序列化数据到一个源吗?
那么反序列化过程肯定会比简单地反映一个新实例更昂贵吗?
还是我错过了什么?
关于装箱和拆箱的时间成本有很多争论……我不确定这算不算。
编辑:这个怎么样(希望这能更好地解释问题)…
Dictionary<string, Type> typecache = new Dictionary<string, Type>();
// finding a type from say a string that points at a type in an assembly not referrenced
// very costly but we can cache that
Type myType = GetSomeTypeWithReflection();
typecache.Add("myType", myType);
// creating an instance you can use very costly
MyThingy thingy = Activator.CreateInstance(typecache["myType"]);
你正在寻找缓存"东西"吗?