Какой лучший способ кешировать дорогие данные, полученные из рефлексии? Например, большинство быстрых сериализаторов кэшируют такую информацию, поэтому им не нужно отражать каждый раз, когда они снова сталкиваются с одним и тем же типом. Они могут даже генерировать динамический метод, который они ищут по типу.
До .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
в качестве ключа. Это должно однозначно идентифицировать этот тип, не сохраняя его в памяти. Я мог бы даже избежать использования ссылочной идентичности в этой строке.
Одна проблема, которая остается с этим решением, состоит в том, что кэшированное значение может также содержать ссылку на тип. И если я использую слабую ссылку для этого, скорее всего, он истекает задолго до того, как сборка будет выгружена. И я не уверен, как дешево получить нормальную ссылку из слабой ссылки. Похоже, мне нужно провести тестирование и тестирование производительности.