GetMethodsの反射のオーバーヘッドは何ですか
-
29-09-2019 - |
質問
書いたいくつかのパーサーの一般的なコードをリファクタリングしました。このコードは、メソッドの実装を自動的に発見するために使用され、既存のパーサーを拡張したり、より乾燥したコードを使用したりするのに非常に便利です(特にこのプロジェクトだけで作業しています):
[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;
}
}
今、私はこのコードをクラスで使用し、頻繁に作成および破壊する可能性があります。
class ClassWhichUsesDiscoveryOnInstanceMethodAndIsShortLived
{
private DynamicCallableMethodTable<string, TSomeDelegate> _table = ...
public ClassWhichUsesDiscoveryOnInstanceMethodAndIsShortLived()
{
_table = new DynamicCallableMethodTable<string, TSomeDelegate>(this, ...);
}
}
そのため、.NET(4.0を使用できます...)の実装内にすでにキャッシュがある場合、または発見プロセスにキャッシュを使用する必要がある場合、GetMethodsのオーバーヘッドをさまよっていました。反射呼び出しがどれほど効率的であるかは本当にわかりません。
解決
@sergeyの次のアイデアに基づいています
はい、MemberInfoキャッシュと呼ばれます。詳細については、msdn.microsoft.com/en-us/magazine/cc163759.aspx - sergey
静的コードを静的クラスに引き出しました。それは、一般的な静的クラスフィールドには独自のスロットがあるという仮定に基づいています(一般的なパラメーターを使用していない場合でも)。 MethodInfoを直接保存すべきではないかどうかはわかりませんが。 Runtimemethodhandleは、長期的にはスペースを節約しているようです。
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;
}
}
所属していません StackOverflow