Question

Je viens refactorisé un morceau commun de code dans plusieurs parseurs je l'ai écrit. Le code est utilisé pour détecter automatiquement les implémentations de méthode et il est assez pratique pour étendre les parseurs existants ou d'utiliser plus de code DRY (en particulier, je travaille sur ce seul projet):

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

Maintenant, je veux utiliser ce code dans une classe qui pourrait être créé et détruit fréquemment:

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

donc je flânais sur les frais généraux de GetMethods, s'il y a déjà une mise en cache à l'intérieur du .NET (4.0 peut être utilisé ...) la mise en œuvre, ou si je dois utiliser la mise en cache pour le processus de découverte. Je suis vraiment pas sûr comment les appels de réflexion efficace sont.

Était-ce utile?

La solution

Basé sur l'idée suivante de @Sergey

  

Oui, il est appelé cache MemberInfo. Plus sur le sujet ici: msdn.microsoft.com/en-us/magazine/cc163759.aspx - Sergey

Je sorti le code statique dans une classe statique, sa base sur l'hypothèse qu'un champ de classe statique générique aura son propre emplacement (même si elle n'utilise pas le paramètre générique?). Bien que je ne suis pas sûr si je ne devrais pas stocker le MethodInfo directement. Le RuntimeMethodHandle semble économiser de l'espace à long terme.

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;
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top