[c#] Caching-Reflektionsdaten



0 Answers

Sie sollten die fasterflect Bibliothek auf Codeplex überprüfen http://fasterflect.codeplex.com/

Sie könnten normale Reflektion verwenden, um dynamisch neuen Code zu generieren und ihn dann zu emittieren / kompilieren und dann die kompilierte Version zwischenzuspeichern. Ich denke, dass die Idee der Sammelmontage viel versprechend ist, um das Speicherleck zu vermeiden, ohne von einer separaten Appdomain geladen / entladen zu werden. Der Speicherverlust sollte jedoch vernachlässigbar sein, wenn Sie nicht Hunderte von Methoden kompilieren.

Hier ist ein Blogpost zum dynamischen Kompilieren von Code zur Laufzeit: http://introspectingcode.blogspot.com/2011/06/dynamically-compile-code-at-runtime.html

Unten ist ein ähnlicher Concurrent-Dictionary-Ansatz, den ich in der Vergangenheit verwendet habe, um die MethodInfo / PropertyInfo-Objekte zu speichern, und es schien schneller zu sein, aber ich denke, das war in einer alten Version von Silverlight. Ich glaube, .NET hat seinen eigenen internen Reflektions-Cache, der es unnötig macht.

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();
        }

    }
}
Question

Was ist der beste Weg, teure Daten aus der Reflektion zu cachen? Zum Beispiel cachen die meisten schnellen Serialisierer solche Informationen zwischenspeichern, so dass sie nicht jedes Mal wieder reflektieren müssen, wenn sie denselben Typ wiederfinden. Sie können sogar eine dynamische Methode generieren, die sie vom Typ nachschlagen.

Bevor .net 4

Traditionell habe ich dafür ein normales statisches Wörterbuch benutzt. Beispielsweise:

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

Dies führt zu ein wenig Speicherverlust, aber da es nur einmal pro Typ und Typen AppDomain solange die AppDomain ich nicht als ein Problem betrachtete.

Seit .net 4

Aber jetzt .net 4 hat Collectable Assemblies für die dynamische Typengenerierung eingeführt. Wenn ich DoSomething für ein Objekt verwendet habe, das in der DoSomething Assembly deklariert ist, wird diese Assembly nie entladen. Autsch.

Also, was ist der beste Weg zum Zwischenspeichern von Informationen pro Typ in. Net 4, die nicht unter diesem Problem leiden? Die einfachste Lösung, die ich mir vorstellen kann, ist:

private static ConcurrentDictionary<WeakReference, TCachedData> cache.

Aber der IEqualityComparer<T> ich arbeiten müsste, würde sich sehr merkwürdig verhalten und würde wahrscheinlich auch den Vertrag verletzen. Ich bin mir nicht sicher, wie schnell die Suche sein würde.

Eine weitere Idee ist die Verwendung eines Ablaufzeitlimits. Könnte die einfachste Lösung sein, fühlt sich aber ein wenig unelegant.

In den Fällen, in denen der Typ als generischer Parameter angegeben wird, kann ich eine verschachtelte generische Klasse verwenden, die nicht unter diesem Problem leiden sollte. Aber sein funktioniert nicht, wenn der Typ in einer Variablen geliefert wird.

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 : Eine Idee, die ich gerade hatte, ist Type.AssemblyQualifiedName als Schlüssel. Das sollte diesen Typ eindeutig identifizieren, ohne ihn im Speicher zu behalten. Ich könnte sogar damit umgehen, die referenzielle Identität für diese Zeichenfolge zu verwenden.

Ein Problem, das bei dieser Lösung bleibt, ist, dass der zwischengespeicherte Wert auch einen Verweis auf den Typ behalten kann. Und wenn ich eine schwache Referenz dafür verwende, wird sie höchstwahrscheinlich lange bevor die Baugruppe entladen wird verfallen. Und ich bin mir nicht sicher, wie billig es ist, aus einer schwachen Referenz eine normale Referenz zu bekommen. Sieht so aus, als müsste ich ein paar Tests und Benchmarks machen.




Related