Frage

ich Refactoring nur ein gemeinsames Stück Code in mehreren Parser habe ich geschrieben. Der Code dient zum automatischen Methode Implementierungen entdecken und es kommt in ganz praktisch die vorhandenen Parser zu erweitern oder mehr DRY-Code zu verwenden (vor allem ich an diesem Projekt arbeite allein):

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class CallableAttribute : Attribute
{
    public CallableAttribute()
        : this(true)
    {
        // intentionally blank
    }

    private CallableAttribute(bool isCallable)
    {
        Callable = isCallable;
    }

    public bool Callable { get; private set; }
}

public class DynamicCallableMethodTable<TClass, THandle>
    where THandle : class
{
    private readonly IDictionary<string, THandle> _table = new Dictionary<string, THandle>();

    public DynamicCallableMethodTable(TClass instance, Func<string, string> nameMangler,
                             BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance)
    {
        var attributeType = typeof(CallableAttribute);
        var classType = typeof(TClass);

        var callableMethods = from methodInfo in classType.GetMethods(bindingFlags)
                              from CallableAttribute a in methodInfo.GetCustomAttributes(attributeType, false)
                              where a.Callable
                              select methodInfo;

        foreach (var method in callableMethods)
            _table[nameMangler(method.Name)] = method.CastToDelegate<THandle>(instance);
    }

    public bool TryGetMethod(string key, out THandle handle)
    {
        return _table.TryGetValue(key, out handle);
    }
}

public static class MethodEx
{
    public static TDelegate CastToDelegate<TDelegate>(this MethodInfo method, object receiver)
        where TDelegate : class
    {
        return Delegate.CreateDelegate(typeof(TDelegate), receiver, method, true) as TDelegate;
    }
}

Jetzt möchte ich diesen Code in einer Klasse verwenden, die erstellt werden können und häufig zerstört:

class ClassWhichUsesDiscoveryOnInstanceMethodAndIsShortLived
{
    private DynamicCallableMethodTable<string, TSomeDelegate> _table = ...
    public ClassWhichUsesDiscoveryOnInstanceMethodAndIsShortLived()
    {
        _table = new DynamicCallableMethodTable<string, TSomeDelegate>(this, ...);
    }
}

so ich wanderte über den Overhead von GetMethods, wenn es bereits einige Caching innerhalb der .NET (4.0 kann verwendet werden, ...) Implementierung, oder ob ich das Caching für den Discovery-Prozess verwenden. Ich bin wirklich nicht sicher, wie effizient die Reflexionsgespräche sind.

War es hilfreich?

Lösung

Auf der Grundlage der folgenden Idee @Sergey

  

Ja, es ist Member Cache genannt. Mehr zu es hier: msdn.microsoft.com/en-us/magazine/cc163759.aspx - Sergey

zog ich den statischen Code in eine statische Klasse aus, das basierte auf der Annahme, dass ein generisches statisches Klassenfeld einen eigenen Steckplatz hat (auch wenn es nicht die allgemeinen Parameter nicht verwendet?). Obwohl ich nicht sicher bin, ob ich direkt die Method nicht gespeichert werden soll. Die Runtime scheint Konserve Raum auf lange Sicht.

static class ReflectionMethodCache<TClass>
{
    /// <summary>
    /// this field gets a different slot for every usage of this generic static class
    /// http://stackoverflow.com/questions/2685046/uses-for-static-generic-classes
    /// </summary>
    private static readonly ConcurrentDictionary<BindingFlags, IList<RuntimeMethodHandle>> MethodHandles;

    static ReflectionMethodCache()
    {
        MethodHandles = new ConcurrentDictionary<BindingFlags, IList<RuntimeMethodHandle>>(2, 5);
    }

    public static IEnumerable<RuntimeMethodHandle> GetCallableMethods(BindingFlags bindingFlags)
    {
        return MethodHandles.GetOrAdd(bindingFlags, RuntimeMethodHandles);
    }

    public static List<RuntimeMethodHandle> RuntimeMethodHandles(BindingFlags bindingFlags)
    {
        return (from methodInfo in typeof (TClass).GetMethods(bindingFlags)
                from CallableAttribute a in
                    methodInfo.GetCustomAttributes(typeof (CallableAttribute), false)
                where a.Callable
                select methodInfo.MethodHandle).ToList();
    }
}

public class DynamicCallableMethodTable<TClass, THandle>
    where THandle : class
{
    private readonly IDictionary<string, THandle> _table = new Dictionary<string, THandle>();

    public DynamicCallableMethodTable(TClass instance, Func<string, string> nameMangler,
                             BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Instance)
    {
        var callableMethods = ReflectionMethodCache<TClass>.GetCallableMethods(bindingFlags);

        foreach (MethodInfo methodInfo in callableMethods.Select(MethodBase.GetMethodFromHandle))
        {
            _table[nameMangler(methodInfo.Name)] = methodInfo.CastToDelegate<THandle>(instance);
        }
    }

    public bool TryGetMethod(string key, out THandle handle)
    {
        return _table.TryGetValue(key, out handle);
    }
}

public static class MethodEx
{
    public static TDelegate CastToDelegate<TDelegate>(this MethodInfo method, object receiver)
        where TDelegate : class
    {
        return Delegate.CreateDelegate(typeof(TDelegate), receiver, method, true) as TDelegate;
    }
}
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top