Mar*_*ade 28
您可以编译表达式树以使用不带任何参数的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 EventHandler<ExampleEventArgs> SomethingHappenedWithArg;
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<T>(EventInfo evt, Action<T> 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<int>(eventInfo2,handler.HandleEventWithArg));
//or even just:
eventinfo.AddEventHandler(raiser,EventProxy.Create(eventinfo,()=>Console.WriteLine("!")));
eventInfo2.AddEventHandler(raiser,EventProxy.Create<int>(eventInfo2,i=>Console.WriteLine(i+"!")));
raiser.RaiseEvents();
}
}
Run Code Online (Sandbox Code Playgroud)
这不是一个完全通用的解决方案,但是如果所有事件的形式都是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!");
}
Run Code Online (Sandbox Code Playgroud)