2012-03-26 29 views
0

是否有可能在运行时生成事件处理程序? 我想要做这样的事情:在运行时生成未知类型的事件处理程序

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; 
} 

回答

0

(我不是100%肯定我明白你要什么,从示例钩子上生成的事件处理程序,但这里是我所知道的用于创建的最简单的方法一个事件处理程序)

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

但是,我发现一个相对容易使用和很好的替代方法来生成Linq表达式(这里是namespace help)。

的想法很简单:

  1. 使用各种表达派生类,你可以在命名空间中看到界定什么回调做。在这种情况下,您想要生成一个在ei实例上调用.RemoveEventHandler(我猜测)的东西(具体来说,您将使用ConstantExpression创建一个对您的ei变量和您的回调参数的引用以及一个MethodCallExpression来创建对方法RemoveDataHandler)。

  2. 一旦创建,它你需要什么,你需要创建一个委托(波长)出它的表达(见here

  3. 差不多完成了。您仍然需要通过您从上一步骤(see here

编辑得到调用对象的.Compile编译拉姆达,你做的:这是一个动态生成的代表,消除自身的Windows控制台的例子。请注意,WP7 Linq表达式支持比.NET 4.0更有限,因此您需要对其进行调整(使帮助器方法可以完成一些工作,并从表达式中调用它们而不是我所做的)。

Edit2:BT​​W:lambda可以移除它自己的机制是创建另一个lambda返回一个局部变量。创建lambda后,将其保存到本地变量并运行代码(我不确定这是否会在没有次级lambda的情况下工作)

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(); 
    } 
} 
+0

感谢您的提示,它非常有用。可悲的是,我不能投票赞成由于缺乏声誉造成的答复。 还是我的问题没有完全解决。为了说清楚,我试图达到的目标,我将重新解释我对该方法的解释。该方法应该将由参数'Callback'定义的'Delegate'作为事件处理程序添加到事件中,由对象'DataContext'的参数'name'命名。在此之后,它应该添加一个EventHandler到'DataContext'的同一个事件中,该事件同时删除'Callback'和它自己。 – Kevkong 2012-03-27 14:23:45

+0

啊。为此,您要使用反射来查找名为“add_”+名称和“remove_”+名称的MethodInfo。这些将是添加和删除事件处理程序的方法。 – 2012-03-27 15:51:20

+0

请参阅我在这里的其他答案。正如我所说,只有一步失踪。我已经使用反射来添加一个事件处理程序,它将删除回调,但我仍然希望创建的方法从事件中删除自己,你现在是什么意思?在正常的代码中,它看起来像这样: Object.CallCompleted + = CallCompleted; Object.CallCompleted + = RemoveHandler; void RemoveHandler(object sender,CompletedEventArgs eA) Object.CallCompleted - = CallCompleted; Object.CallCompleted - = RemoveHandler; } – Kevkong 2012-03-27 21:09:47

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; 
} 

这就是我的方法看起来像现在。只有一步丢失,新的事件处理程序(由System.Linq.Expressions.Expression.Lambda(ei.EventHandlerType, call, parameters).Compile()创建)删除它自己(请参阅评论)。

0

感谢沙哈尔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; 
    } 

正如我前面所说,这种方法应该由Delegate Callback添加Eventhandler传递给名为static MAServiceClient DataProxystring nameEvent和之后将其删除被称为(和Eventhandler它自己删除回调)。

相关问题