您如何动态订阅C#事件,以便给定一个Object实例和一个包含事件名称的String名称,您订阅该事件并在触发该事件时执行某些操作(例如写入控制台)?
它似乎使用反射,这是不可能的,我想避免必要时使用Reflection.Emit,因为这当前(对我来说)似乎是这样做的唯一方法.
/编辑:我不知道事件所需的代表签名,这是问题的核心
/编辑2:尽管代表性逆转似乎是一个好的计划,但我无法做出使用此解决方案所必需的假设
您可以编译表达式树以使用不带任何参数的void方法作为任何类型事件的事件处理程序.要容纳其他事件处理程序类型,您必须以某种方式将事件处理程序的参数映射到事件.
using System; using System.Linq; using System.Linq.Expressions; using System.Reflection; class ExampleEventArgs : EventArgs { public int IntArg {get; set;} } class EventRaiser { public event EventHandler SomethingHappened; public event EventHandlerSomethingHappenedWithArg; public void RaiseEvents() { if (SomethingHappened!=null) SomethingHappened(this, EventArgs.Empty); if (SomethingHappenedWithArg!=null) { SomethingHappenedWithArg(this, new ExampleEventArgs{IntArg = 5}); } } } class Handler { public void HandleEvent() { Console.WriteLine("Handler.HandleEvent() called.");} public void HandleEventWithArg(int arg) { Console.WriteLine("Arg: {0}",arg); } } static class EventProxy { //void delegates with no parameters static public Delegate Create(EventInfo evt, Action d) { var handlerType = evt.EventHandlerType; var eventParams = handlerType.GetMethod("Invoke").GetParameters(); //lambda: (object x0, EventArgs x1) => d() var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x")); var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke")); var lambda = Expression.Lambda(body,parameters.ToArray()); return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false); } //void delegate with one parameter static public Delegate Create (EventInfo evt, Action d) { var handlerType = evt.EventHandlerType; var eventParams = handlerType.GetMethod("Invoke").GetParameters(); //lambda: (object x0, ExampleEventArgs x1) => d(x1.IntArg) var parameters = eventParams.Select(p=>Expression.Parameter(p.ParameterType,"x")).ToArray(); var arg = getArgExpression(parameters[1], typeof(T)); var body = Expression.Call(Expression.Constant(d),d.GetType().GetMethod("Invoke"), arg); var lambda = Expression.Lambda(body,parameters); return Delegate.CreateDelegate(handlerType, lambda.Compile(), "Invoke", false); } //returns an expression that represents an argument to be passed to the delegate static Expression getArgExpression(ParameterExpression eventArgs, Type handlerArgType) { if (eventArgs.Type==typeof(ExampleEventArgs) && handlerArgType==typeof(int)) { //"x1.IntArg" var memberInfo = eventArgs.Type.GetMember("IntArg")[0]; return Expression.MakeMemberAccess(eventArgs,memberInfo); } throw new NotSupportedException(eventArgs+"->"+handlerArgType); } } static class Test { public static void Main() { var raiser = new EventRaiser(); var handler = new Handler(); //void delegate with no parameters string eventName = "SomethingHappened"; var eventinfo = raiser.GetType().GetEvent(eventName); eventinfo.AddEventHandler(raiser,EventProxy.Create(eventinfo,handler.HandleEvent)); //void delegate with one parameter string eventName2 = "SomethingHappenedWithArg"; var eventInfo2 = raiser.GetType().GetEvent(eventName2); eventInfo2.AddEventHandler(raiser,EventProxy.Create (eventInfo2,handler.HandleEventWithArg)); //or even just: eventinfo.AddEventHandler(raiser,EventProxy.Create(eventinfo,()=>Console.WriteLine("!"))); eventInfo2.AddEventHandler(raiser,EventProxy.Create (eventInfo2,i=>Console.WriteLine(i+"!"))); raiser.RaiseEvents(); } }
这不是一个完全通用的解决方案,但是如果所有事件的形式都是void Foo(object o,T args),其中T派生自EventArgs,那么你可以使用委托逆变来逃避它.像这样(KeyDown的签名与Click的签名不同):
public Form1() { Button b = new Button(); TextBox tb = new TextBox(); this.Controls.Add(b); this.Controls.Add(tb); WireUp(b, "Click", "Clickbutton"); WireUp(tb, "KeyDown", "Clickbutton"); } void WireUp(object o, string eventname, string methodname) { EventInfo ei = o.GetType().GetEvent(eventname); MethodInfo mi = this.GetType().GetMethod(methodname, BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic); Delegate del = Delegate.CreateDelegate(ei.EventHandlerType, this, mi); ei.AddEventHandler(o, del); } void Clickbutton(object sender, System.EventArgs e) { MessageBox.Show("hello!"); }