Создает делегат из MethodInfo?
После поиска и посадки на SO и прочтения этого другого вопроса
Возможно ли построить правильный делегат из MethodInfo, если вы не знали количество или типы параметров во время компиляции?
Подробнее об этом: можно ли это сделать элегантно без использования Reflection.Emit или типа строителей?
Это сортировка для меня, потому что Delegate.CreateDelegate требует, чтобы я указал правильный тип делегирования в качестве первого параметра, иначе он будет генерировать исключения или вызывать неправильный метод.
Я строю несколько передач ниндзя, и это очень поможет... Спасибо!
Вот общее решение:
/// <summary>
/// Builds a Delegate instance from the supplied MethodInfo object and a target to invoke against.
/// </summary>
public static Delegate ToDelegate(MethodInfo mi, object target)
{
if (mi == null) throw new ArgumentNullException("mi");
Type delegateType;
var typeArgs = mi.GetParameters()
.Select(p => p.ParameterType)
.ToList();
// builds a delegate type
if (mi.ReturnType == typeof(void)) {
delegateType = Expression.GetActionType(typeArgs.ToArray());
} else {
typeArgs.Add(mi.ReturnType);
delegateType = Expression.GetFuncType(typeArgs.ToArray());
}
// creates a binded delegate if target is supplied
var result = (target == null)
? Delegate.CreateDelegate(delegateType, mi)
: Delegate.CreateDelegate(delegateType, target, mi);
return result;
}
Примечание. Я создаю приложение Silverlight, которое заменило бы встроенное javascript-приложение, встроенное в предыдущее время, в котором у меня есть несколько интерфейсов Javascript, которые вызывают один и тот же метод Silverlight [ScriptableMember].
Все поддерживаемые интерфейсы JS должны поддерживаться, а также новый интерфейс для доступа к новым функциям, поэтому что-то, что автоматически настраивает интерфейс JS и "делегирует" вызов правильному методу Silverlight, поможет значительно ускорить работу.
Я не могу отправить код здесь, так что резюме.
Ответы
Ответ 1
Если честно, если вы не знаете тип во время компиляции, нет никакого большого преимущества при создании Delegate
. Вы не хотите использовать DynamicInvoke
; он будет примерно таким же медленным, как отражение. Основное исключение - когда в тенях скрывается тип делегата, например, при подписке на событие - в этом случае EventInfo
делает это доступным.
Для информации в .NET 3.5 на Expression
есть:
Expression.GetActionType(params Type[] typeArgs);
Expression.GetFuncType(params Type[] typeArgs)
Это может помочь в некоторой степени:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
static class Program {
static void Main() {
DoStuff("Test1");
DoStuff("Test2");
}
static void DoStuff(string methodName) {
MethodInfo method = typeof(Program).GetMethod(methodName);
List<Type> args = new List<Type>(
method.GetParameters().Select(p => p.ParameterType));
Type delegateType;
if (method.ReturnType == typeof(void)) {
delegateType = Expression.GetActionType(args.ToArray());
} else {
args.Add(method.ReturnType);
delegateType = Expression.GetFuncType(args.ToArray());
}
Delegate d = Delegate.CreateDelegate(delegateType, null, method);
Console.WriteLine(d);
}
public static void Test1(int i, DateTime when) { }
public static float Test2(string x) { return 0; }
}
Ответ 2
Если вы заранее не знаете количество или тип параметров, возможно, это означает, что вы не знаете тип делегата, который хотите создать?
Если это случай, вы застряли в абсолютно общем случае.
Однако для большинства распространенных случаев (без параметров ref/out, достаточно нескольких параметров для использования одного из существующих типов) вы можете уйти с одним из делегатов Func
или Action
. (.NET 4.0 имеет типы Func
/Action
для огромного количества параметров, так что вам действительно нужно будет беспокоиться о параметрах out/ref.) Если метод имеет невоидный тип возврата, используйте Func
, в противном случае используйте Action
. Определите, какой тип использовать в зависимости от количества параметров, например
static readonly Type[] FuncTypes = { typeof(Func),
typeof(Func<>), typeof(Func<,>), typeof(Func<,,>), /* etc */ };
Используйте Type.MakeGenericType
, используя типы параметров и тип возвращаемого значения, чтобы получить правильный тип делегата, тогда Delegate.CreateDelegate
должен работать.
У меня нет времени, чтобы подготовить образец прямо сейчас, но дайте мне знать, если вы хотите, чтобы я позже.
Один вопрос: как вы собираетесь использовать этот делегат? Что-то еще нужно знать, как его выполнить, конечно...
Ответ 3
Почему это сложно?
public static Delegate CreateDelegate(this MethodInfo method)
{
return Delegate.CreateDelegate
(
Expression.GetDelegateType
(
method.GetParameters()
.Select(p => p.ParameterType)
.Concat(new Type[] { method.ReturnType })
.ToArray()
),
null,
method
);
}
[Боковое примечание: я префикс этого метода "Создать...". "To..." сбивает с толку, поскольку он вводит вас в заблуждение, чтобы думать, что это конверсия.]