2009-07-14 2 views
32

После прибегая к помощи и посадки на SO и прочтя this other questionСоздает делегата из MethodInfo?

Можно ли построить правильный делегат из 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-интерфейсы, которые вызывают один и тот же метод Silverlight [ScriptableMember].

Необходимо поддерживать все эти унаследованные JS-интерфейсы, а также новый интерфейс для доступа к новым функциям, поэтому что-то, что автоматически настраивает интерфейс JS и «делегирует» вызов правильному методу Silverlight, поможет значительно ускорить работу.

Я не могу отправить здесь код, так что это резюме.

ответ

22

Если честно, если вы не знаете тип во время компиляции, нет никакого большого преимущества при создании 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; } 
} 
+1

Я создаю это, чтобы склеить Silverlight [ScriptableMember] и отдельный интерфейс Javascript, поэтому мне не нужно беспокоиться о синхронизации сигнатур методов в обоих местоположениях. – chakrit

+0

Ничего себе .... это помогло! Вы, ребята, рок! – chakrit

+0

@ Marc Gravell, я не могу вызвать делегата, созданного в приведенном выше коде, например d(). После googling я обнаружил, что dynamicInvoke может использоваться для вызова метода, который очень медленный. PLS помощи. Я новичок в делегатах и ​​мероприятиях. мое требование заключается в том, чтобы динамически вызывать метод, число или тип параметров будут известны только во время выполнения. – Saranya

7

Если вы заранее не знаете количество или тип параметров, возможно, это означает, что вы не знаете тип делегата, который хотите создать либо?

Если это так, вы застряли в абсолютно общем случае.

Однако для большинства общих случаев (не реф/из параметров, достаточно несколько параметров, чтобы использовать один из существующих типов), вы могли бы уйти с одного из делегатов 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 должен работать.

У меня нет времени на обработку образца прямо сейчас, но дайте мне знать, если вы хотите, чтобы я позже.

Один вопрос: как вы собираетесь использовать этот делегат? Что-то еще нужно знать, как его выполнить, конечно ...

+0

ah-ha ... хорошо возьмите MakeGenericType + Func ... что бы это сделать :-) – chakrit

+4

Чтобы избежать static Тип [], рассмотрим Expression.GetActionType/Expression.GetFuncType - см. сообщение. Я бы * надеюсь, что эти методы были расширены, чтобы включить новые версии .NET 4.0. –

+0

Я добавил «почему» на вопрос – chakrit

6

Почему это сложно?

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 ...» сбивает с толку, поскольку он вводит вас в заблуждение, думая, что это конверсия.]

+0

Метод Expression.GetDelegateType на самом деле специфичен для .NET 4 и SL 4. Мой вопрос задавали до выпуска .NET4 и SL4. В любом случае спасибо за ответ. Обратите внимание, что вам нужно связать делегата с целью, если метод является методом экземпляра, поэтому обязательная часть по-прежнему является обязательной. – chakrit

+0

Передача объекта в качестве второго параметра (называемого «firstArgument» - здесь я передаю null), вы можете указать объект, к которому привязан делегат. Или я не понимаю смысла? – 0xbadf00d

Смежные вопросы