c# 利用反射清除事件

控件的事件清除,除了-=,就只能依靠反射来执行了。

/// <summary>
/// 清除一个对象的某个事件所挂钩的delegate
/// </summary>
/// <param name="ctrl">控件对象</param>
/// <param name="eventName">事件名称,默认的</param>
public static void ClearEvents(this object ctrl, string eventName = "_EventAll")
{
    if (ctrl == null) return;
    BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.IgnoreCase | BindingFlags.Static;
    EventInfo[] events = ctrl.GetType().GetEvents(bindingFlags);
    if (events == null || events.Length < 1) return;

    for (int i = 0; i < events.Length; i++)
    {
        try
        {
            EventInfo ei = events[i];
            //只删除指定的方法,默认是_EventAll,前面加_是为了和系统的区分,防以后雷同
            if (eventName != "_EventAll" && ei.Name != eventName) continue;

            /********************************************************
             * class的每个event都对应了一个同名(变了,前面加了Event前缀)的private的delegate类
             * 型成员变量(这点可以用Reflector证实)。因为private成
             * 员变量无法在基类中进行修改,所以为了能够拿到base 
             * class中声明的事件,要从EventInfo的DeclaringType来获取
             * event对应的成员变量的FieldInfo并进行修改
             ********************************************************/
            FieldInfo fi = ei.DeclaringType.GetField("Event" + ei.Name, bindingFlags);
            if (fi != null)
            {
                // 将event对应的字段设置成null即可清除所有挂钩在该event上的delegate
                fi.SetValue(ctrl, null);
            }
        }
        catch { }
    }
}

当前使用环境.net 4.0。参考了很多其他人的代码,有三个地方值得注意。

一个是eventName,GetField的时候在原来的Name前面加"Event"前缀。这个可能在不同的.net版本不一样,出现过三种:eventName,"Event"+eventName, "Event_" + controlType.Name + eventname.

第二个:BindingFlags。尽量用Public | NonPublic | Instance | IgnoreCase | Static。

第三:GetField的执行对象用EventInfo.DeclaringType,否则有可能继承的类获取不到数据。

 

补充一个测试。下面的代码中Button会执行三次-=然后+=,但是每次的对象都是重新new的。三次绑定后,点击button1时,只执行一次输出。

public Form1()
{    
    this.Load += (a, b) =>
    {
        new Thread(new ThreadStart(() =>
        {
            int i = 0;
            while (true)
            {
                if (button1.InvokeRequired) button1.Invoke(new Action(ReBindEvent));
                else ReBindEvent();
                Thread.Sleep(100);
                i++;
                if (i >= 3) break;
            }
        })).Start();
    };
}

void ReBindEvent()
{
    button1.Click -= GetClickHandle();
    button1.Click += GetClickHandle();
}

EventHandler GetClickHandle()
{
    return new EventHandler((a, b) =>
    {
        Console.WriteLine(DateTime.Now + "  执行一次");
    });
} 

 

posted @ 2014-10-23 16:31  脸谱匠  阅读(7372)  评论(3编辑  收藏  举报