C# 反射:如何在不知道委托签名的情况下订阅事件?
C# reflection: How to subscribe to a event without knowing delegate signature?
我想构建一个小适配器 class,运行 时间解释器可以用它拦截事件的发生,而不管委托类型如何。
我试过这样的事情(失败并在构造函数中出现 运行time-exception):
public class ModelEvent
{
private object source;
private EventInfo sourceEvent;
private Delegate eventHandler;
public event EventHandler OnEvent;
public ModelEvent(object source, EventInfo sourceEvent)
{
this.source = source;
this.sourceEvent = sourceEvent;
// this line is responsible for the error,
// the signatures of the eventHandler and delegate method don't match
this.eventHandler = Delegate.CreateDelegate(
this.sourceEvent.EventHandlerType,
this,
typeof(ModelEvent).GetMethod("OnSourceEvent"));
// Alternative failed attempts
// this.eventHandler = new Action<object, object> (this.OnSourceEvent);
// this.eventHandler = Delegate.CreateDelegate(typeof(EventHandler), this, typeof(ModelEvent).GetMethod("OnSourceEvent"));
this.sourceEvent.AddEventHandler(this.source, this.eventHandler);
}
private void OnSourceEvent(object sender, object eventArgs)
{
this.OnEvent?.Invoke(this, EventArgs.Empty);
}
// ... truncated for clarity
}
这在 运行 时崩溃,因为我的 OnSourceEvent
方法的 (object, object)
签名与通用 EventHandler<T>
委托的任何 (object, T)
签名不匹配给定事件 sourceEvent
.
所以我想我需要在 运行 时用正确的签名构造某种包装器?
我对 C# 中的泛型还很陌生,如果我的解释是正确的,我将不胜感激寻找构建可行委托的方法的任何帮助。非常感谢!
感谢阅读material。但是,我找到了解决问题的方法 。
诀窍是在运行时动态创建一个 lambda 表达式来包装我的“无参数”事件并将其用作委托。
正确的代码如下所示。使用它,您可以订阅任何事件并在事件发生时得到通知(尽管您不会收到任何 EventArgs
):
public class ModelEvent
{
private object source;
private EventInfo sourceEvent;
private Delegate eventHandler;
public event EventHandler OnEvent;
public ModelEvent(object source, EventInfo sourceEvent)
{
this.source = source;
this.sourceEvent = sourceEvent;
ParameterExpression[] sourceEventHandlerParameters =
sourceEvent
.EventHandlerType
.GetMethod("Invoke")
.GetParameters()
.Select(parameter => Expression.Parameter(parameter.ParameterType))
.ToArray();
MethodCallExpression fireOnEvent = Expression.Call(
Expression.Constant(new Action(FireOnEvent)),
"Invoke",
Type.EmptyTypes);
this.eventHandler = Expression.Lambda(
sourceEvent.EventHandlerType,
fireOnEvent,
sourceEventHandlerParameters)
.Compile();
this.sourceEvent.AddEventHandler(this.source, this.eventHandler);
}
private void FireOnEvent()
{
this.OnEvent?.Invoke(this, EventArgs.Empty);
}
}
如果你知道 Expression
class 我想你可以去掉 FireOnEvent
方法并以某种方式直接调用 this.OnEvent?.Invoke(this, EventArgs.Empty)
,但我懒得去找出如何动态构建该调用。
我想构建一个小适配器 class,运行 时间解释器可以用它拦截事件的发生,而不管委托类型如何。
我试过这样的事情(失败并在构造函数中出现 运行time-exception):
public class ModelEvent
{
private object source;
private EventInfo sourceEvent;
private Delegate eventHandler;
public event EventHandler OnEvent;
public ModelEvent(object source, EventInfo sourceEvent)
{
this.source = source;
this.sourceEvent = sourceEvent;
// this line is responsible for the error,
// the signatures of the eventHandler and delegate method don't match
this.eventHandler = Delegate.CreateDelegate(
this.sourceEvent.EventHandlerType,
this,
typeof(ModelEvent).GetMethod("OnSourceEvent"));
// Alternative failed attempts
// this.eventHandler = new Action<object, object> (this.OnSourceEvent);
// this.eventHandler = Delegate.CreateDelegate(typeof(EventHandler), this, typeof(ModelEvent).GetMethod("OnSourceEvent"));
this.sourceEvent.AddEventHandler(this.source, this.eventHandler);
}
private void OnSourceEvent(object sender, object eventArgs)
{
this.OnEvent?.Invoke(this, EventArgs.Empty);
}
// ... truncated for clarity
}
这在 运行 时崩溃,因为我的 OnSourceEvent
方法的 (object, object)
签名与通用 EventHandler<T>
委托的任何 (object, T)
签名不匹配给定事件 sourceEvent
.
所以我想我需要在 运行 时用正确的签名构造某种包装器?
我对 C# 中的泛型还很陌生,如果我的解释是正确的,我将不胜感激寻找构建可行委托的方法的任何帮助。非常感谢!
感谢阅读material。但是,我找到了解决问题的方法
诀窍是在运行时动态创建一个 lambda 表达式来包装我的“无参数”事件并将其用作委托。
正确的代码如下所示。使用它,您可以订阅任何事件并在事件发生时得到通知(尽管您不会收到任何 EventArgs
):
public class ModelEvent
{
private object source;
private EventInfo sourceEvent;
private Delegate eventHandler;
public event EventHandler OnEvent;
public ModelEvent(object source, EventInfo sourceEvent)
{
this.source = source;
this.sourceEvent = sourceEvent;
ParameterExpression[] sourceEventHandlerParameters =
sourceEvent
.EventHandlerType
.GetMethod("Invoke")
.GetParameters()
.Select(parameter => Expression.Parameter(parameter.ParameterType))
.ToArray();
MethodCallExpression fireOnEvent = Expression.Call(
Expression.Constant(new Action(FireOnEvent)),
"Invoke",
Type.EmptyTypes);
this.eventHandler = Expression.Lambda(
sourceEvent.EventHandlerType,
fireOnEvent,
sourceEventHandlerParameters)
.Compile();
this.sourceEvent.AddEventHandler(this.source, this.eventHandler);
}
private void FireOnEvent()
{
this.OnEvent?.Invoke(this, EventArgs.Empty);
}
}
如果你知道 Expression
class 我想你可以去掉 FireOnEvent
方法并以某种方式直接调用 this.OnEvent?.Invoke(this, EventArgs.Empty)
,但我懒得去找出如何动态构建该调用。