Генерация Eventhandler неизвестного типа во время работы - PullRequest
0 голосов
/ 26 марта 2012

возможно ли генерировать обработчик событий во время работы? Я хочу сделать что-то подобное:

public bool addCallback(string name, Delegate Callback)
{
   EventInfo ei = DataProxy.GetType().GetEvent(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
   if (ei == null)
      return false;
   ei.AddEventHandler(DataProxy, Callback);
   //now I want to add an Eventhandler, which removes the Callback and this new Eventhandler itsself
   return true;
}

Ответы [ 3 ]

0 голосов
/ 27 марта 2012
public static bool addCallback(string name, Delegate Callback)
{
    if (DataProxy == null)
        GetDataProxy();
    EventInfo ei = DataProxy.GetType().GetEvent(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
    if (ei == null)
        return false;
    ei.AddEventHandler(DataProxy, Callback);
    Type handlerType = ei.EventHandlerType;
    MethodInfo invokeMethod = handlerType.GetMethod("Invoke");
    ParameterInfo[] parms = invokeMethod.GetParameters();
    Type[] parmTypes = new Type[parms.Length];
    for (int i = 0; i < parms.Length; i++)
    {
        parmTypes[i] = parms[i].ParameterType;
    }
    List<ParameterExpression> parameters = new List<ParameterExpression>();
    foreach(Type t in parmTypes)
    {
        parameters.Add(System.Linq.Expressions.Expression.Parameter(t));
    }
    ConstantExpression eventInfo = System.Linq.Expressions.Expression.Constant(ei, typeof(EventInfo));
    ConstantExpression eventCallback = System.Linq.Expressions.Expression.Constant(Callback, typeof(Delegate));
    ConstantExpression dataProxy = System.Linq.Expressions.Expression.Constant(DataProxy, typeof(MAServiceClient));
    MethodCallExpression call = System.Linq.Expressions.Expression.Call(eventInfo, ei.GetType().GetMethod("RemoveEventHandler"), dataProxy, eventCallback);
    //add to Expression.Body the call, which removes the new Eventhandler itsself
    ei.AddEventHandler(DataProxy, System.Linq.Expressions.Expression.Lambda(ei.EventHandlerType, call, parameters).Compile());
    return true;
}

Вот так выглядит мой метод прямо сейчас.Не хватает только одного шага, когда новый Eventhandler (созданный System.Linq.Expressions.Expression.Lambda(ei.EventHandlerType, call, parameters).Compile()) удаляет себя (см. Комментарий).

0 голосов
/ 30 марта 2012

Благодаря Shahar Prish я получил следующий код:

using ex = System.Linq.Expressions;
using System.Linq.Expressions;

    public static bool addCallback(string name, Delegate Callback)
    {
        if (DataProxy == null)
            GetDataProxy();
        EventInfo ei = DataProxy.GetType().GetEvent(name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
        if (ei == null)
            return false;
        ei.AddEventHandler(DataProxy, Callback);
        Type handlerType = ei.EventHandlerType;
        MethodInfo removeMethod = ei.GetType().GetMethod("RemoveEventHandler");
        MethodInfo invokeMethod = handlerType.GetMethod("Invoke");
        ParameterInfo[] parms = invokeMethod.GetParameters();
        Type[] parmTypes = new Type[parms.Length];
        for (int i = 0; i < parms.Length; i++)
        {
            parmTypes[i] = parms[i].ParameterType;
        }
        List<ParameterExpression> parameters = new List<ParameterExpression>();
        foreach(Type t in parmTypes)
        {
            parameters.Add(System.Linq.Expressions.Expression.Parameter(t));
        }
        Delegate self = null;
        Func<Delegate> getSelf = () => self;
        ConstantExpression eventInfo = ex.Expression.Constant(ei, typeof(EventInfo));
        ConstantExpression eventCallback = ex.Expression.Constant(Callback, typeof(Delegate));
        ConstantExpression dataProxy = ex.Expression.Constant(DataProxy, typeof(MAServiceClient));
        MethodCallExpression removeCallback = ex.Expression.Call(eventInfo, removeMethod, dataProxy, eventCallback);
        MethodCallExpression removeSelf = ex.Expression.Call(eventInfo, removeMethod, dataProxy, ex.Expression.Invoke(ex.Expression.Constant(getSelf)));
        BlockExpression block = ex.Expression.Block(removeCallback, removeSelf);
        LambdaExpression lambda = ex.Expression.Lambda(ei.EventHandlerType, block, parameters);
        Delegate del = lambda.Compile();
        self = del;
        ei.AddEventHandler(DataProxy, del);
        lambda = ex.Expression.Lambda(ei.EventHandlerType, block, parameters);
        return true;
    }

Как я уже говорил, этот метод должен добавить Eventhandler, переданный Delegate Callback к Event с именем string name из static MAServiceClient DataProxy и удалить его после его вызова (и Eventhandler, который удаляет сам обратный вызов).

0 голосов
/ 26 марта 2012

(я не уверен на 100%, что понимаю, к чему вы собираетесь подключить сгенерированный обработчик событий из примера, но вот самый простой способ, которым я знаю для создания обработчика событий)

Зависит от вашей платформы и уровня доверия. Наиболее гибкий способ сделать это - использовать Emit для генерации метода (см. здесь ).

Однако я нашел относительно простую в использовании и хорошую альтернативу для генерации выражений Linq (вот справка для пространства имен ).

Идея довольно проста:

  1. Используйте различные производные от Expression классы, которые вы можете видеть в пространстве имен, чтобы определить, что делает ваш обратный вызов. В этом случае вы хотите сгенерировать что-то, что вызывает .RemoveEventHandler (я предполагаю) на экземпляре ei (в частности, вы будете использовать ConstantExpression , чтобы создать ссылку на вашу переменную ei и к вашему параметру Callback и MethodCallExpression для создания вызова метода RemoveDataHandler).

  2. Как только вы создадите выражение, которое делает то, что вам нужно, вам нужно создать из него делегат (лямбда) (см. здесь )

  3. Почти готово. Вам все еще нужно скомпилировать лямбду, которую вы делаете, вызывая .Compile для объекта, полученного на предыдущем шаге ( см. Здесь )

Редактировать: Это пример консоли Windows для динамически генерируемого делегата, который удаляет себя. Обратите внимание, что поддержка выражений WP7 Linq более ограничена, чем .NET 4.0, и поэтому вам нужно будет ее настроить (создать вспомогательные методы, которые выполнят часть работы, и вызвать их из выражения вместо того, что я делал).

Edit2: BTW: механизм, с помощью которого лямбда может удалить себя, заключается в создании другой лямбды, которая возвращает локальную переменную этого типа. После создания лямбды сохраните его в локальной переменной и запустите код (я не уверен, что это работало бы без дополнительной лямбды)

Edit3: Нет - вы должны использовать трюк делегата, в противном случае константа «замораживается» и не будет обновляться так, как вы этого хотите. Так что код как есть работает.

public class MyEventArgs : EventArgs
{
}

public class EventContainer
{
    public event EventHandler<MyEventArgs> MyEvent;

    public void Fire()
    {
        Console.WriteLine("Firing");
        if (MyEvent != null)
        {
            MyEvent(this, new MyEventArgs());
        }
        Console.WriteLine("Fired");
    }
}

class Program
{
    static void Main(string[] args)
    {
        EventContainer container = new EventContainer();
        var adder = container.GetType().GetMethod("add_MyEvent");
        var remover = container.GetType().GetMethod("remove_MyEvent");

        object self = null;
        Func<object> getSelf = () => self;

        var block = Expression.Block(
            // Call something to output to console.
            Expression.Call(
                null,
                typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) }),
                Expression.Constant("Event called")),
            // Now call the remove_Event method.
            Expression.Call(
                Expression.Constant(container), // passing the container as "this"
                remover, // And the remover as the method info
                Expression.Convert( // we need to cast the result of getSelf to the correct type to pass as an argument
                    Expression.Invoke( // For the parameter (what to convert), we need to call getSelf
                        Expression.Constant(getSelf)), // So this is a ref to getSelf
                    adder.GetParameters()[0].ParameterType) // finally, say what to convert to.
               ) 
           );

        // Create a lambda of the correct type.
        var lambda = Expression.Lambda(
            adder.GetParameters()[0].ParameterType, 
            block, 
            Expression.Parameter(typeof(object)), 
            Expression.Parameter(typeof(MyEventArgs)));
        var del = lambda.Compile();

        // Make sure "self" knows what the delegate is (so our generated code can remove it)
        self = del;


        // Add the event.
        adder.Invoke(container, new object[] { del });

        // Fire once - see that delegate is being called.
        container.Fire();

        // Fire twice - see that the delegate was removed.
        container.Fire();
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...