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