简体   繁体   English

C# 反射:如何在不知道委托签名的情况下订阅事件?

[英]C# reflection: How to subscribe to a event without knowing delegate signature?

I'd like to construct a little adapter class with which a run-time interpreter may intercept the occurance of events regardless of delegate type.我想构建一个小适配器 class 运行时解释器可以拦截事件的发生,而不管委托类型如何。

I've tried something like this (which fails with a runtime-exception in the constructor):我已经尝试过这样的事情(在构造函数中出现运行时异常而失败):

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
}

This crashes at runtime, since the (object, object) signature of my OnSourceEvent method does not match any (object, T) signature of the generic EventHandler<T> delegate of the given event sourceEvent .这在运行时崩溃,因为我的OnSourceEvent方法的(object, object)签名与给定事件sourceEvent的通用EventHandler<T>委托的任何(object, T)签名都不匹配。

So I guess I need to construct some sort of wrapper with the correct signature at runtime?所以我想我需要在运行时构建某种具有正确签名的包装器?

I'm quite new at generics in C# and would appreciate any help in finding a way to construct a viable Delegate, if my interpretation is correct.我是 C# 的 generics 的新手,如果我的解释正确,我将不胜感激找到构建可行代表的方法。 Thanks a lot!非常感谢!

Thanks for the reading material.感谢阅读材料。 However, I've found the solution to my problem in this SO-question .但是,我在这个 SO-question 中找到了我的问题的解决方案。

The trick is to dynamically create a lambda-expressions at runtime to wrap my "parameterless" events and use that as the delegate.诀窍是在运行时动态创建一个 lambda 表达式来包装我的“无参数”事件并将其用作委托。

The correct code looks like this.正确的代码如下所示。 Using it, you can subscribe to any event and get notified when it is raised (although you won't get any EventArgs ):使用它,您可以订阅任何事件并在它引发时收到通知(尽管您不会收到任何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);
    }
}

If you know your way around the Expression class I guess you can eliminate the FireOnEvent method and somehow directly call this.OnEvent?.Invoke(this, EventArgs.Empty) , but I was too lazy to figure out how to construct that call dynamically.如果您知道Expression class 的方式,我想您可以消除FireOnEvent方法并以某种方式直接调用this.OnEvent?.Invoke(this, EventArgs.Empty) ,但我懒得弄清楚如何动态构造该调用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM