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 + "  執行一次");
    });
} 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM