Если вы знаете подпись, используйте Delegate.CreateDelegate
.
Если вы не знаете подпись, очень сложно получить что-то такое быстрое.Если вам нужна скорость, то, что бы вы ни делали, старайтесь избегать Delegate.DynamicInvoke
, который чрезвычайно медленный.
(Обратите внимание, что «медленный» здесь очень относительный. Убедитесь, что вам действительно нужно оптимизировать это. DynamicInvoke
это примерно 2,5 миллиона вызовов в секунду (на моей машине), что, скорее всего, достаточно быстро. Реализация ниже более похожа на 110+ миллионов вызовов в секунду и быстрее, чем Method.Invoke
.)
Я обнаружил статья , в которой обсуждается способ сделать это (быстро вызвать метод, не зная сигнатуру во время компиляции).Вот моя версия реализации.Странная проблема заключается в том, что вы можете создать лямбду, которая представляет вызов, но вы не будете знать сигнатуру этой лямбды и должны будете вызывать ее динамически (медленно).Но вместо этого вы можете испечь строго типизированный вызов в лямбду, причем лямбда представляет собой акт вызова, а не конкретный метод.(Лямбда заканчивается как Func<object, object[], object>
, где вы передаете объект и некоторые значения и возвращаете возвращаемое значение.)
public static Func<object, object[], object> ToFastLambdaInvocationWithCache(
this MethodInfo pMethodInfo
) {
Func<object, object[], object> cached;
if (sLambdaExpressionsByMethodInfoCache.TryGetValue(pMethodInfo, out cached))
return cached;
var instanceParameterExpression = Expression.Parameter(typeof(object), "instance");
var argumentsParameterExpression = Expression.Parameter(typeof(object[]), "args");
var index = 0;
var argumentExtractionExpressions =
pMethodInfo
.GetParameters()
.Select(parameter =>
Expression.Convert(
Expression.ArrayAccess(
argumentsParameterExpression,
Expression.Constant(index++)
),
parameter.ParameterType
)
).ToList();
var callExpression = pMethodInfo.IsStatic
? Expression.Call(pMethodInfo, argumentExtractionExpressions)
: Expression.Call(
Expression.Convert(
instanceParameterExpression,
pMethodInfo.DeclaringType
),
pMethodInfo,
argumentExtractionExpressions
);
var endLabel = Expression.Label(typeof(object));
var finalExpression = pMethodInfo.ReturnType == typeof(void)
? (Expression)Expression.Block(
callExpression,
Expression.Return(endLabel, Expression.Constant(null)),
Expression.Label(endLabel, Expression.Constant(null))
)
: Expression.Convert(callExpression, typeof(object));
var lambdaExpression = Expression.Lambda<Func<object, object[], object>>(
finalExpression,
instanceParameterExpression,
argumentsParameterExpression
);
var compiledLambda = lambdaExpression.Compile();
sLambdaExpressionsByMethodInfoCache.AddOrReplace(pMethodInfo, compiledLambda);
return compiledLambda;
}