问题描述
我正在尝试将委托附加到不同委托的调用列表.通过这种方式,我正在实现一种对现有事件的钩子.我需要连接在每个被调用的事件之后运行的东西.
i am attempting to attach a Delegate to an invocation list of a different delegate. By that i am achieving a kind of Hook on existing events. I need to hook up something that runs after each event that is invoked.
只要类型暴露的委托和我传入的动作具有完全相同的签名,以下示例有效.(On1 和 OnAll 事件都使用 Action 委托声明,因此可以正常工作).
The following example works as long as the Delegate exposed by the type and the Action i pass in have the exact same signature. (On1 and OnAll events are both declared with an Action delegate so it works).
代码:我如何将 Action 与事件修饰符公开的现有委托挂钩.
Code : How i hook up an Action with an existing delegate exposed by an event modifier.
public static class ReflectionExtensions
{
public static IEnumerable<EventInfo> GetEvents(this object obj)
{
var events = obj.GetType().GetEvents();
return events;
}
public static void AddHandler(this object obj, Action action)
{
var events = obj.GetEvents();
foreach (var @event in events)
{
@event.AddEventHandler(obj, action);
}
}
}
样本:
public class Tester
{
public event Action On1;
public event Action On2;
public void RaiseOn1()
{
On1();
}
public void RaiseOn2()
{
On2();
}
}
class Program
{
static void Main(string[] args)
{
var t = new Tester();
t.On1 += On1;
t.On2 += On2;
t.AddHandler(OnAll);
t.RaiseOn1();
t.RaiseOn2();
}
public void On1() { }
public void On2() { }
public void OnAll() { }
}
问题:当在 Tester 中使用事件修饰符公开的委托没有相同的签名时,我会得到一个非常想要且明显的异常,它指出(用我的话)Action
不能添加到 Action<int>
的调用列表中.说得通.
The Problem : When the Delegate exposed with an event modifier in Tester does not have the same signature i get a well wanted and obvious exception which states (in my words) that Action
can't be added to an invocation list of an Action<int>
. makes sense.
为了清楚起见,我正在描述以下内容:
Just to be clear I'm describing the following :
public event Action<int> On1;
public void On1(int i){}
我正在寻找一种方法来创建与 EventHandlerType 相同类型的另一个委托.为此,我需要创建一个带有 EventHandlerType 签名 i 的方法,该方法将在内部调用操作.
What I'm looking for is a way to create another Delegate of the same type as the EventHandlerType. In order to do that i need to create a method with the signature i of EventHandlerType which would internally invoke action.
类似:
public static void AddHandler(this object obj, Action action)
{
var events = obj.GetEvents();
foreach (var @event in events)
{
// method with the signeture of EventHandlerType which does action();
MethodInfo wrapperMethod = WrapAction(@event.EventHandlerType, action);
Delegate handler = Delegate.CreateDelegate(@event.EventHandlerType, action.Target, wrapperMethod);
@event.AddEventHandler(obj, handler);
}
}
推荐答案
这似乎可行...里面有各种评论...我不确定这是否是最好的方法.我正在构建一个 Expression
树来执行委托调用.
This seems to work... There are various comments inside... I'm not sure if this is the best way to do it. I'm building an Expression
tree to do the delegate invocation.
public static void AddHandler(this object obj, Action action)
{
var events = obj.GetEvents();
foreach (var @event in events)
{
// Simple case
if (@event.EventHandlerType == typeof(Action))
{
@event.AddEventHandler(obj, action);
}
else
{
// From here: http://stackoverflow.com/a/429564/613130
// We retrieve the parameter types of the event handler
var parameters = @event.EventHandlerType.GetMethod("Invoke").GetParameters();
// We convert it to ParameterExpression[]
ParameterExpression[] parameters2 = Array.ConvertAll(parameters, x => Expression.Parameter(x.ParameterType));
MethodCallExpression call;
// Note that we are "opening" the delegate and using
// directly the Target and the Method! Inside the
// LambdaExpression we will build there won't be a
// delegate call, there will be a method call!
if (action.Target == null)
{
// static case
call = Expression.Call(action.Method);
}
else
{
// instance type
call = Expression.Call(Expression.Constant(action.Target), action.Method);
}
// If you are OK to create a delegate that calls another
// delegate, you can:
// call = Expression.Call(Expression.Constant(action), typeof(Action).GetMethod("Invoke"));
// instead of the big if/else
var lambda = Expression.Lambda(@event.EventHandlerType, call, parameters2);
@event.AddEventHandler(obj, lambda.Compile());
}
}
}
这篇关于反射 - 将委托添加到另一个委托的调用列表的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!