garbage collector c# como funciona
Almacenamiento en caché de datos de reflexión (3)
Debería consultar la biblioteca de fasterflect en codeplex http://fasterflect.codeplex.com/
Puede usar la reflexión normal para generar código nuevo dinámicamente y luego emitir / compilar y luego almacenar en caché la versión compilada. Creo que la idea del ensamblaje coleccionable es prometedora, para evitar la pérdida de memoria sin tener que cargar / descargar desde un dominio de aplicación separado. Sin embargo, la pérdida de memoria debe ser insignificante a menos que esté compilando cientos de métodos.
Aquí hay un blog sobre compilación dinámica de código en tiempo de ejecución: http://introspectingcode.blogspot.com/2011/06/dynamically-compile-code-at-runtime.html
A continuación se muestra un enfoque similar de diccionario concurrente que he usado en el pasado para almacenar los objetos MethodInfo / PropertyInfo y parecía ser más rápido, pero creo que estaba en una versión anterior de Silverlight. Creo que .Net tiene su propio caché interno de reflexión que lo hace innecesario.
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();
}
}
}
¿Cuál es la mejor manera de almacenar en caché datos costosos obtenidos de la reflexión? Por ejemplo, la mayoría de los serializadores rápidos almacenan en caché dicha información para que no tengan que reflejarse cada vez que encuentran el mismo tipo de nuevo. Incluso pueden generar un método dinámico que buscan desde el tipo.
Antes .net 4
Tradicionalmente he usado un diccionario estático normal para eso. Por ejemplo:
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
}
}
Esto pierde un poco de memoria, pero como lo hace solo una vez por Tipo y tipos vividos, siempre y cuando el AppDomain
la AppDomain
no lo considere un problema.
Desde .net 4
Pero ahora .net 4 introdujo Conjuntos coleccionables para la Generación dinámica de tipos . Si alguna vez utilicé DoSomething
en un objeto declarado en el ensamblaje coleccionable, ese ensamblaje nunca se descargará. Ay.
Entonces, ¿cuál es la mejor manera de almacenar en caché por tipo de información en .NET 4 que no sufre este problema? La solución más fácil que puedo pensar es una:
private static ConcurrentDictionary<WeakReference, TCachedData> cache.
Pero el IEqualityComparer<T>
que tendría que usar con eso se comportaría de manera muy extraña y probablemente también violaría el contrato. No estoy seguro de cuán rápido sería la búsqueda tampoco.
Otra idea es usar un tiempo de expiración. Podría ser la solución más simple, pero se siente un poco poco elegante.
En los casos en que se proporciona el tipo como parámetro genérico, puedo usar una clase genérica anidada que no debería sufrir este problema. Pero el suyo no funciona si el tipo se proporciona en una variable.
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.
}
}
Actualización : Una idea que acabo de tener es utilizar Type.AssemblyQualifiedName
como clave. Eso debería identificar de manera única ese tipo sin mantenerlo en la memoria. Incluso podría salirse con la suya usando identidad referencial en esta cadena.
Un problema que queda con esta solución es que el valor en caché también puede mantener una referencia al tipo. Y si uso una referencia débil para eso probablemente caduque mucho antes de que se descargue el ensamblado. Y no estoy seguro de cuán barato es Obtener una referencia normal de una referencia débil. Parece que necesito hacer algunas pruebas y puntos de referencia.
Podría estar diciendo lo obvio aquí, pero:
¿Los proveedores de caché no suelen serializar los datos a una fuente?
Entonces, ¿el proceso de deserialización va a ser más costoso que simplemente reflejar una nueva instancia?
O perdí algo?
Y está todo el argumento sobre los costos del tiempo del boxeo y el desempaquetado ... aunque no estoy seguro de si eso realmente cuenta.
Editar:
¿Qué tal esto? (Espero que esto explique el problema un poco mejor) ...
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"]);
¿Estás buscando caché "thingy"?
ConcurrentDictionary<WeakReference, CachedData>
es incorrecto en este caso. Supongamos que estamos tratando de almacenar en caché la información para el tipo T, por lo que WeakReference.Target==typeof(T)
. CachedData muy probablemente contendrá la referencia para typeof(T)
también. Como ConcurrentDictionary<TKey, TValue>
almacena elementos en la colección interna de Node<TKey, TValue>
, tendrá una cadena de referencias sólidas: instancia ConcurrentDictionary
-> instancia de Node
-> propiedad Value
(instancia CachedData
) -> typeof(T)
. En general, es imposible evitar la pérdida de memoria con WeakReference en el caso en que los valores puedan tener referencias a sus claves.
Fue necesario agregar soporte para efemerones para hacer posible tal escenario sin pérdidas de memoria. Afortunadamente .NET 4.0 los admite y tenemos la clase ConditionalWeakTable<TKey, TValue>
. Parece que las razones para presentarlo están cerca de tu tarea.
Este enfoque también resuelve el problema mencionado en su actualización, ya que la referencia a Type durará exactamente el tiempo que se cargue Assembly.