3

I just refactored a common piece of code in several parsers I have written. The code is used to automatically discover method implementations and it comes in quite handy to extend the existing parsers or to use more DRY code (especially I am working on this project alone):

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

Now I want to use this code in a class which might be created and destroyed frequently:

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

so I was wandering over the overhead of GetMethods, if there is already some caching inside the .NET (4.0 can be used ...) implementation, or if I should use caching for the discovery process. I am really unsure how efficient the reflection calls are.

Sebastian
  • 6,293
  • 6
  • 34
  • 47
  • sorry i wanted to use auto-completion of the tags but i somehow posted to early ^^ also i found out reflection is not spelt reflexion ^^ – Sebastian Nov 29 '10 at 04:55
  • Could you clarify what you mean by "class which might be created and destroyed frequently"? Do you mean that `DynamicCallableMethodTable` instances are typically short-lived? Since the type appears immutable to me, why not cache instances of it? – Ani Nov 29 '10 at 05:05
  • DynamicCallableMethodTable also works on instance methods (so the instance parameter would be an instance of the method, i will edit for an example – Sebastian Nov 29 '10 at 05:10
  • I can't use the debugger for the frequent case, as its used in a Server quite far away, so I don't think I can really time efficiently, I was more hoping to get inside behind the .NET implementation, if there is already sufficient caching going on, then I dont want to repeat the caching logic... – Sebastian Nov 29 '10 at 05:16
  • 1
    Yes, it's called MemberInfo cache. More on it here: http://msdn.microsoft.com/en-us/magazine/cc163759.aspx –  Nov 29 '10 at 05:19

1 Answers1

0

Based on the following idea of @Sergey

Yes, it's called MemberInfo cache. More on it here: msdn.microsoft.com/en-us/magazine/cc163759.aspx – Sergey

I pulled out the static code into a static class, its based on the assumption that a generic static class field will have its own slot (even though it does not use the generic parameter?). Although I am not sure if I shouldn't store the MethodInfo Directly. The RuntimeMethodHandle seems to conserve space in the long run.

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;
    }
}
Sebastian
  • 6,293
  • 6
  • 34
  • 47
  • Nice, SO can read the link in the code and links it, how amazing! :) Linked Uses for static generic classes? – Sebastian Nov 29 '10 at 07:35