我试图在事件发生时触发一个操作,忽略事件参数(至少目前是这样)。我通过反射找到事件,然后创建一个与预期签名匹配的动态方法(不能保证只有Sender/EventArgs),然后尝试调用该操作。
/// <summary>
/// Execute an action when an event fires, ignoring it's parameters.
/// 'type' argument is element.GetType()
/// </summary>
bool TryAsEvent(Type type, UIElement element, string actionStr, Action act)
{
try
{
//Get event info
var eventInfo = type.GetEvent(actionStr); //Something like 'Click'
var eventType = eventInfo.EventHandlerType;
//Get parameters
var methodInfo = eventType.GetMethod("Invoke");
var parameterInfos = methodInfo.GetParameters();
var paramTypes = parameterInfos.Select((info => info.ParameterType)).ToArray();
//Create method
var dynamicMethod = new DynamicMethod("", typeof(void), paramTypes);
//Static method that will invoke the Action (from our parameter 'act')
//Necessary because the action itself wasn't recognized as a static method
// which caused an InvalidProgramException
MethodInfo exec = typeof(ThisClass).GetMethod("ExecuteEvent");
//Generate IL
var il = dynamicMethod.GetILGenerator();
il.DeclareLocal(typeof(MethodInfo));
//MethodInfo miExecuteAction = act.Method;
//Commented out some trial and failure stuff
il.Emit(OpCodes.Ldobj, act.Method);
//il.Emit(OpCodes.Stloc, lc);
//il.Emit(OpCodes.Ldloc, lc);
//il.Emit(OpCodes.Ldobj, act.Method);
//il.Emit(OpCodes.Ldarg_0);
//il.Emit(OpCodes.Pop);
il.EmitCall(OpCodes.Call, exec, null);
il.Emit(OpCodes.Ret);
//Test the method (the event under test has a handler taking 2 args):
//var act2 = dynamicMethod.CreateDelegate(eventType);
//act2.DynamicInvoke(new object[]{null, null});
//Add handler
var handler = dynamicMethod.CreateDelegate(eventType);
eventInfo.AddEventHandler(element, handler);
return true;
}
catch
{
return false;
}
}
public static void ExecuteEvent(MethodInfo i)
{
i.Invoke(null, null);
}
有人能告诉我如何做到这一点吗?
更新:这是一个模仿真实场景的简洁的VS11项目文件:
Download
更新(修复):
public class Program
{
public static void Main(string[] args)
{
var x = new Provider();
new Program().TryAsEvent(
x.GetType(),
x,
"Click",
new Program().TestInstanceMethod);
//Use this lambda instead to test a static action
//() => Console.WriteLine("Action fired when event did."));
x.Fire();
Console.ReadLine();
}
public void TestInstanceMethod()
{
Console.WriteLine("Action fired when event did.");
}
/// <summary>
/// Execute an action when an event fires, ignoring it's parameters.
/// </summary>
bool TryAsEvent(Type type, object element, string actionStr, Action act)
{
try
{
var getMFromH = typeof(MethodBase)
.GetMethod("GetMethodFromHandle",
BindingFlags.Public | BindingFlags.Static,
null,
new[] { typeof(RuntimeMethodHandle) }, null);
//Get event info
var eventInfo = type.GetEvent(actionStr);
var eventType = eventInfo.EventHandlerType;
//Get parameters
var methodInfo = eventType.GetMethod("Invoke");
var parameterInfos = methodInfo.GetParameters();
var paramTypes = parameterInfos.Select((info => info.ParameterType)).ToArray();
//Non-static action?
var target = act.Target;
if (target != null) //Prepend instance object
paramTypes = new[] {target.GetType()}.Union(paramTypes).ToArray();
//Create method
var dynamicMethod = new DynamicMethod("", typeof(void), paramTypes);
//Static method that will invoke the Action (from our parameter 'act')
var exec = typeof (Program).GetMethod
(target != null // Call proper method based on scope of action
? "ExecuteEvent"
: "ExecuteEventStati");
//Generate IL
var il = dynamicMethod.GetILGenerator();
if (target != null) //Push object instance on stack if working with non-static action
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldtoken, act.Method);
il.Emit(OpCodes.Call, getMFromH);
il.Emit(OpCodes.Call, exec);
il.Emit(OpCodes.Ret);
//Add handler
var handler =
target != null
//Call with target obj if we're working with a non-static action
? dynamicMethod.CreateDelegate(eventType, target)
: dynamicMethod.CreateDelegate(eventType);
eventInfo.AddEventHandler(element, handler);
return true;
}
catch (Exception ex)
{
Console.WriteLine("Exception: " + ex);
return false;
}
}
public static void ExecuteEventStati(MethodInfo i)
{
i.Invoke(null, null);
}
public static void ExecuteEvent(object o, MethodInfo i)
{
i.Invoke(o, null);
}
}
下面是该示例的无关代码(以防有人想要复制和粘贴):
public class Provider
{
public event MyRoutedEventHandler Click;
public void Fire()
{
if (Click != null)
Click(this, new MyRoutedEventArgs());
}
}
public delegate void MyRoutedEventHandler(object sender, MyRoutedEventArgs e);
public class MyRoutedEventArgs : RoutedEventArgs
{
public MyRoutedEventArgs()
{
}
public MyRoutedEventArgs(RoutedEvent routedEvent)
: this(routedEvent, (object)null)
{
}
public MyRoutedEventArgs(RoutedEvent routedEvent, object source)
: base(routedEvent, source){}
}