2010-11-29 2 views
3

Je viens de refactoriser un morceau de code commun dans plusieurs analyseurs que j'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 le code plus sec (surtout je travaille sur ce seul projet):Quel est l'overhead de réflexion dans GetMethods

[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 j'errait 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 devrais utiliser la mise en cache pour le processus de découverte. Je ne sais pas vraiment à quel point les appels de réflexion sont efficaces.

+0

désolé je voulais utiliser l'auto-complétion des balises mais je en quelque sorte posté au début ^^ aussi j'ai découvert la réflexion n'est pas orthographié ^^ réflexion – Sebastian

+0

Pouvez-vous préciser ce que vous entendez par « classe qui pourrait être créé et détruit fréquemment "? Voulez-vous dire que les instances de DynamicCallableMethodTable 'sont généralement de courte durée? Puisque le type m'apparaît immuable, pourquoi ne pas en cacher les instances? – Ani

+0

DynamicCallableMethodTable fonctionne également sur les méthodes d'instance (donc le paramètre instance serait une instance de la méthode, je vais éditer pour un exemple – Sebastian

Répondre

0

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 tirai 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 slot (même s'il 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; 
    } 
} 
+0

Nice, SO peut lire le lien dans le code et le relie, c'est incroyable ! :) Lié Utilisations pour les classes génériques statiques? – Sebastian

+0

semble bien fonctionner – Sebastian

Questions connexes