I just reworked the general part of the code in several parsers that I wrote. The code is used to automatically detect the implementation of the method, and it is very convenient for expanding existing parsers or for using more DRY code (especially I work on this project myself):
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class CallableAttribute : Attribute
{
public CallableAttribute()
: this(true)
{
}
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 that can be created and destroyed often:
class ClassWhichUsesDiscoveryOnInstanceMethodAndIsShortLived
{
private DynamicCallableMethodTable<string, TSomeDelegate> _table = ...
public ClassWhichUsesDiscoveryOnInstanceMethodAndIsShortLived()
{
_table = new DynamicCallableMethodTable<string, TSomeDelegate>(this, ...);
}
}
so I wandered about GetMethods overhead if there is already caching inside .NET (4.0 can be used ...), or if I have to use caching for the discovery process. I'm really not sure how effective callbacks are.