繁体   English   中英

通过反射C#添加和删除事件处理程序

[英]Add and remove event handler via reflection c#

美好的一天! 我的目的是实现类,该类将允许我们向(从)事件订阅和取消订阅对象。 这是我班上的代码。

public static class EventSubscriber
{
    public static void AddEventHandler(EventInfo eventInfo, object item, Action action)
    {
        var parameters = GetParameters(eventInfo);
        var handler = GetHandler(eventInfo, action, parameters);
        eventInfo.AddEventHandler(item, handler);
    }

    public static void RemoveEventHandler(EventInfo eventInfo,
                        object item, Action action)
    {
        var parameters = GetParameters(eventInfo);
        var handler = GetHandler(eventInfo, action, parameters);
        eventInfo.RemoveEventHandler(item, handler);
    }

    private static ParameterExpression[] GetParameters(EventInfo eventInfo)
    {
        return eventInfo.EventHandlerType
          .GetMethod("Invoke")
          .GetParameters()
          .Select(parameter => Expression.Parameter(parameter.ParameterType))
          .ToArray();
    }

    private static Delegate GetHandler(EventInfo eventInfo,
                Action action, ParameterExpression[] parameters)
    {
        return Expression.Lambda(
            eventInfo.EventHandlerType,
            Expression.Call(Expression.Constant(action),
                      "Invoke", Type.EmptyTypes), parameters)
          .Compile();
    }
}

如您所见,这里有2个公共方法,它们实际上向(从)事件订阅和取消订阅对象。 这是我如何测试的示例

class Program
{
    static void Main()
    {
        Test test = new Test();
        test.SubscribeTimer();
        while (true)
        {
            if(test.a == 10)
            {
                break;
            }
        }
        test.UnsubscribeTimer();
        while (true)
        {

        }
    }
}

class Test
{
    System.Timers.Timer timer;
    public int a = 0;

    public Test()
    {
        timer = new System.Timers.Timer(1000);
        timer.Start();
    }

    public void SubscribeTimer()
    {
        var eventInfo = typeof(System.Timers.Timer).GetEvent("Elapsed");
        EventSubscriber.AddEventHandler(eventInfo, timer, TimerElapsed);
        EventSubscriber.RemoveEventHandler(eventInfo, timer, TimerNotElapsed);
    }

    public void UnsubscribeTimer()
    {
        var eventInfo = typeof(System.Timers.Timer).GetEvent("Elapsed");
        EventSubscriber.AddEventHandler(eventInfo, timer, TimerNotElapsed);
        EventSubscriber.RemoveEventHandler(eventInfo, timer, TimerElapsed);
    }

    public void TimerElapsed()
    {
        Console.WriteLine("timer elapsed");
        a++;
    }

    public void TimerNotElapsed()
    {
        Console.WriteLine("timer not elapsed");
        a++;
    }
}

样本的预期行为是,在一开始,我们将每秒看到消息“计时器已过去”,在10秒之后,我们应该仅看到“计时器未过去”,但是,我们仍然看到“计时器已过去”。 这意味着AddEventHandler方法有效,但RemoveEventHandler方法无效。

如果您能帮助我,我将非常高兴。 提前致谢。

我认为这是因为您每次都在创建一个新的处理程序:( 与先前的处理程序不匹配,因此无法从调用列表中删除)

public static void RemoveEventHandler(EventInfo eventInfo,
                    object item, Action action)
{
    var parameters = GetParameters(eventInfo);
    var handler = GetHandler(eventInfo, action, parameters); // <--
    eventInfo.RemoveEventHandler(item, handler);
}

你为什么要包装Action 要丢失参数? 无法添加/删除eventInfo.RemoveEventHandler(item, action); 由于参数。 如果要删除新生成的处理程序,则在删除它时应返回该处理程序。

public static Delegate AddEventHandler(EventInfo eventInfo, object item, Action action)
{
    var parameters = GetParameters(eventInfo);
    var handler = GetHandler(eventInfo, action, parameters);
    eventInfo.AddEventHandler(item, handler);
    return handler;
}

public static void RemoveEventHandler(EventInfo eventInfo,
                    object item, Delegate handler)
{
    eventInfo.RemoveEventHandler(item, handler);
}

var handler = EventSubscriber.AddEventHandler(eventInfo, timer, TimerElapsed);

EventSubscriber.RemoveEventHandler(eventInfo, timer, handler);

为了将我需要的东西缝合在一起,需要一站式扩展方法(如果我们更换组件供应商,可以轻松修改),以清除我们无法控制的事件上的所有现有事件处理程序。

用法很简单:

thirdPartyControlInstance.ClearEventHandlers(nameof(ThirdPartyControlType.ToolClick));

这是代码,欢迎提出任何使其更健壮/高效/清洁的建议:

public static class EventExtensions
{
    public static void ClearEventHandlers(this object obj, string eventName)
    {
        if (obj == null)
        {
            return;
        }

        var objType = obj.GetType();
        var eventInfo = objType.GetEvent(eventName);
        if (eventInfo == null)
        {
            return;
        }

        var isEventProperty = false;
        var type = objType;
        FieldInfo eventFieldInfo = null;
        while (type != null)
        {
            /* Find events defined as field */
            eventFieldInfo = type.GetField(eventName, BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
            if (eventFieldInfo != null && (eventFieldInfo.FieldType == typeof(MulticastDelegate) || eventFieldInfo.FieldType.IsSubclassOf(typeof(MulticastDelegate))))
            {
                break;
            }

            /* Find events defined as property { add; remove; } */
            eventFieldInfo = type.GetField("EVENT_" + eventName.ToUpper(), BindingFlags.Static | BindingFlags.Instance | BindingFlags.NonPublic);
            if (eventFieldInfo != null)
            {
                isEventProperty = true;
                break;
            }

            type = type.BaseType;
        }

        if (eventFieldInfo == null)
        {
            return;
        }

        if (isEventProperty)
        {
            // Default Events Collection Type
            RemoveHandler<EventHandlerList>(obj, eventFieldInfo);
            // Infragistics Events Collection Type
            RemoveHandler<EventHandlerDictionary>(obj, eventFieldInfo);
            return;
        }

        if (!(eventFieldInfo.GetValue(obj) is Delegate eventDelegate))
        {
            return;
        }

        // Remove Field based event handlers
        foreach (var d in eventDelegate.GetInvocationList())
        {
            eventInfo.RemoveEventHandler(obj, d);
        }
    }

    private static void RemoveHandler<T>(object obj, FieldInfo eventFieldInfo)
    {
        var objType = obj.GetType();
        var eventPropertyValue = eventFieldInfo.GetValue(obj);

        if (eventPropertyValue == null)
        {
            return;
        }

        var propertyInfo = objType.GetProperties(BindingFlags.NonPublic | BindingFlags.Instance)
                                  .FirstOrDefault(p => p.Name == "Events" && p.PropertyType == typeof(T));
        if (propertyInfo == null)
        {
            return;
        }

        var eventList = propertyInfo?.GetValue(obj, null);
        switch (eventList) {
            case null:
                return;
            case EventHandlerDictionary typedEventList:
                typedEventList.RemoveHandler(eventPropertyValue, typedEventList[eventPropertyValue]);
                break;
        }
    }
}

希望这对某人有帮助!

暂无
暂无

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

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