运行时生成未知类型的Eventhandler

时间:2012-03-26 19:35:57

标签: c# silverlight

是否可以在运行时生成事件处理程序? 我想做那样的事情:

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 :(得分:0)

(我不是100%确定我理解你要将生成的事件处理程序挂钩到示例中,但这是我知道创建事件处理程序的最简单方法)

取决于您的平台和信任级别。最灵活的方法是使用Emit生成方法(请参阅here)。

然而,我找到了一个相对容易使用且很好的替代方法来生成Linq表达式(这里是namespace help)。

这个想法很简单:

  1. 使用您可以在命名空间中看到的各种Expression派生类来定义回调正在执行的操作。在这种情况下,您希望在.RemoveEventHandler实例上生成调用ei(我猜)的内容(具体来说,您将使用ConstantExpression为您的{{1}创建引用变量和您的Callback参数以及MethodCallExpression来创建对ei方法的调用。

  2. 创建满足需要的表达式后,需要创建一个委托(Lambda)(参见here

  3. 差不多完成了。您仍然需要编译lambda,通过在上一步(see here

  4. 上获得的对象上调用RemoveDataHandler来执行此操作

    编辑:这是一个动态生成的委托的Windows控制台示例,用于删除自身。请注意,WP7 Linq表达式支持比.NET 4.0更受限制,因此您需要对其进行调整(制作辅助方法以执行某些工作并从表达式调用它们而不是我所做的)。

    Edit2:BT​​W:lambda可以自行删除的机制是创建另一个返回该类型的局部变量的lambda。创建lambda之后,将它保存到局部变量并运行代码(我不确定这是否可以在没有辅助lambda的情况下工作)

    Edit3:不 - 你必须使用委托技巧,否则,常量会被“冻结”,并且不会像你想要的那样更新。因此代码可行。

    .Compile

答案 1 :(得分:0)

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()创建)将自行删除(请参阅注释)。

答案 2 :(得分:0)

感谢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的{​​{1}},然后将其删除被调用(以及static MAServiceClient DataProxy删除了自己的回调)。

相关问题