避免重复注册事件引起的内存泄漏

偶遇需要避免重复注册事件引起的内存泄漏

这个问题会出现在数据绑定到界面的时候,由于重新New了一个Element去显示而造成的事件注册没有取消

此项已经更新,主要在于当前Field查找字段的时候只能查找当前Type的类型,现更改为可以向上查找父级,返回第一个查找到的字段,那么在注册的时候就不用指定写继承了INotifyPropertyChanged 或者 INotifyCollectionChanged的类型了

  1 /* 迹I柳燕
  2  * 
  3  * FileName:   JisonsINotifyOfInvocationList.cs
  4  * Version:    1.0
  5  * Date:       2014.03.18
  6  * Author:     Ji
  7  * 
  8  *========================================
  9  * @namespace  Jisons 
 10  * @class      JisonsINotifyOfInvocationList
 11  * @extends    
 12  *             
 13  *             WPF 扩展
 14  *             对于实现 INotify 接口的委托链 增 删 委托的处理
 15  * 
 16  *========================================
 17  * Hi,小喵喵...
 18  * Copyright © 迹I柳燕
 19  * 
 20  * 转载请保留...
 21  * 
 22  */
 23 
 24 using System;
 25 using System.Collections.Specialized;
 26 using System.ComponentModel;
 27 using System.Linq;
 28 using System.Reflection;
 29 
 30 namespace Jisons
 31 {
 32     public static class JisonsINotifyOfInvocationList
 33     {
 34 
 35         /// <summary> 增加委托到 _invocationList 委托列表中 ,执行重复检查</summary>
 36         /// <typeparam name="T">具有 _invocationList 链的类型</typeparam>
 37         /// <param name="data">具有线程通知的数据,此项为原委托链的数据类型</param>
 38         /// <param name="handler">增加到委托链的委托</param>
 39         /// <param name="isINotifyCollectionChanged">当前类型是否为 INotifyCollectionChanged</param>
 40         /// <returns>执行增加是否成功</returns>
 41         static bool JudgeAddEventHandler<T>(this object data, Delegate handler, bool isINotifyCollectionChanged)
 42         {
 43             bool canAdd_invocation = false;
 44 
 45             if (data == null)
 46             {
 47                 return canAdd_invocation;
 48             }
 49             try
 50             {
 51                 var collectionchangedValue = handler;
 52                 var flag = BindingFlags.Instance | BindingFlags.NonPublic;
 53 
 54                 string changed = string.Empty;
 55                 FieldInfo judgechanged = null;
 56                 if (isINotifyCollectionChanged)
 57                 {
 58                     changed = "CollectionChanged";
 59                     judgechanged = typeof(T).FindField(changed, flag);
 60                 }
 61                 else
 62                 {
 63                     changed = "PropertyChanged";
 64                     judgechanged = typeof(T).FindField(changed, flag);
 65                 }
 66 
 67                 if (judgechanged == null)
 68                 {
 69                     return canAdd_invocation;
 70                 }
 71 
 72                 var collectionvalue = judgechanged.GetValue(data);
 73                 if (collectionvalue != null)
 74                 {
 75                     var collectionlist = typeof(MulticastDelegate).FindField("_invocationList", flag).GetValue(collectionvalue) as object[];
 76                     if (collectionlist != null)
 77                     {
 78                         #region Judge Distinct
 79 
 80                         //In This Way To Remove The Same Handler 
 81                         //At Fact, I Want To Remove The Handler When I Add If ( collectionlist == null)
 82 
 83                         var collectionlistdistinct = collectionlist.Distinct().ToList();
 84                         int collectioncount = collectionlist.Count() - 1;
 85                         for (int i = collectioncount; i >= 0; i--)
 86                         {
 87                             var item = collectionlist[i];
 88                             if (item != null)
 89                             {
 90                                 if (collectionlistdistinct.Contains(item))
 91                                 {
 92                                     collectionlistdistinct.Remove(item);
 93                                 }
 94                                 else
 95                                 {
 96                                     Delegate collectionchangedValueTemp = null;
 97                                     if (isINotifyCollectionChanged)
 98                                     {
 99                                         collectionchangedValueTemp = item as NotifyCollectionChangedEventHandler;
100                                     }
101                                     else
102                                     {
103                                         collectionchangedValueTemp = item as PropertyChangedEventHandler;
104                                     }
105 
106                                     if (collectionchangedValueTemp != null)
107                                     {
108                                         var method = typeof(T).GetEvent(changed);
109                                         if (method != null)
110                                         {
111                                             method.RemoveEventHandler(data, collectionchangedValueTemp);
112                                         }
113                                     }
114                                 }
115                             }
116                         }
117 
118                         #endregion
119 
120                         if (collectionlist != null && !collectionlist.Contains(collectionchangedValue))
121                         {
122                             canAdd_invocation = true;
123                         }
124                     }
125 
126                     //In The Second To Add Handle , Can Not Find The Data Of collectionlist
127                     else
128                     {
129                         var method = typeof(T).GetEvent(changed);
130                         method.RemoveEventHandler(data, collectionchangedValue);
131                         method.AddEventHandler(data, collectionchangedValue);
132                     }
133                 }
134                 else
135                 {
136                     canAdd_invocation = true;
137                 }
138 
139                 if (canAdd_invocation)
140                 {
141                     var method = typeof(T).GetEvent(changed);
142                     method.AddEventHandler(data, collectionchangedValue);
143                 }
144             }
145             catch
146             {
147                 throw new Exception("Add Event Handler Unsuccess ...");
148             }
149 
150             return canAdd_invocation;
151         }
152 
153         /// <summary> 清空具有 _invocationList 的委托列表 </summary>
154         /// <typeparam name="T">具有 _invocationList 链的类型</typeparam>
155         /// <param name="data">具有线程通知的数据</param>
156         /// <param name="remove_targetdata">删除指定类型中的所有此类型注册委托</param>
157         /// <param name="isINotifyCollectionChanged">当前类型是否为 INotifyCollectionChanged</param>
158         /// <returns>执行删除是否成功</returns>
159         static bool JudgeRemoveEventHandler<T>(this object data, object remove_targetdata, bool isINotifyCollectionChanged)
160         {
161             if (data == null)
162             {
163                 return false;
164             }
165 
166             bool canRemove_invocation = false;
167 
168             var flag = BindingFlags.Instance | BindingFlags.NonPublic;
169 
170             string changed = string.Empty;
171             FieldInfo judgechanged = null;
172             if (isINotifyCollectionChanged)
173             {
174                 changed = "CollectionChanged";
175                 judgechanged = typeof(T).FindField(changed, flag);
176             }
177             else
178             {
179                 changed = "PropertyChanged";
180                 judgechanged = typeof(T).FindField(changed, flag);
181             }
182 
183             if (judgechanged == null)
184             {
185                 return canRemove_invocation;
186             }
187 
188             var collectionvalue = judgechanged.GetValue(data);
189             if (collectionvalue != null)
190             {
191                 var collectionlist = typeof(MulticastDelegate).FindField("_invocationList", flag).GetValue(collectionvalue) as object[];
192                 if (collectionlist != null)
193                 {
194                     int collectioncount = collectionlist.Count() - 1;
195                     for (int i = collectioncount; i >= 0; i--)
196                     {
197                         var item = collectionlist[i];
198                         if (item != null)
199                         {
200                             var target = typeof(Delegate).FindField("_target", flag).GetValue(item);
201                             bool canRemove = target == null || target.Equals(remove_targetdata);
202                             if (canRemove)
203                             {
204                                 Delegate collectionchangedValueTemp = null;
205                                 if (isINotifyCollectionChanged)
206                                 {
207                                     collectionchangedValueTemp = item as NotifyCollectionChangedEventHandler;
208                                 }
209                                 else
210                                 {
211                                     collectionchangedValueTemp = item as PropertyChangedEventHandler;
212                                 }
213 
214                                 if (collectionchangedValueTemp == null)
215                                 {
216                                     return canRemove_invocation;
217                                 }
218 
219                                 var method = typeof(T).GetEvent(changed);
220                                 method.RemoveEventHandler(data, collectionchangedValueTemp);
221                                 canRemove_invocation = true;
222                             }
223                         }
224                     }
225                 }
226             }
227 
228             return canRemove_invocation;
229         }
230 
231         /// <summary> 对 INotifyPropertyChanged 的委托链增加委托函数 </summary>
232         /// <typeparam name="T">具有线程通知的传入类型</typeparam>
233         /// <param name="property">实现线程通知的类型</param>
234         /// <param name="handler">将要增加的委托</param>
235         /// <returns>是否增加成功</returns>
236         public static bool AddPropertyEventHandler<T>(this INotifyPropertyChanged property, PropertyChangedEventHandler handler)
237         {
238             return JudgeAddEventHandler<T>(property, handler, false);
239         }
240 
241         /// <summary> 对 INotifyCollectionChanged 的委托链增加委托函数 </summary>
242         /// <typeparam name="T">具有线程通知的传入类型</typeparam>
243         /// <param name="collection">实现线程通知的类型</param>
244         /// <param name="handler">将要增加的委托</param>
245         /// <returns>是否增加成功</returns>
246         public static bool AddCollectionEventHandler<T>(this INotifyCollectionChanged collection, NotifyCollectionChangedEventHandler handler)
247         {
248             return JudgeAddEventHandler<T>(collection, handler, true);
249         }
250 
251         /// <summary> 对 INotifyPropertyChanged 的委托链删除指定Target的委托函数 </summary>
252         /// <typeparam name="T">具有线程通知的传入类型</typeparam>
253         /// <param name="property">将要增加的委托</param>
254         /// <param name="remove_targetdata"></param>
255         /// <returns>是否删除成功</returns>
256         public static bool RemovePropertyEventHandler<T>(this INotifyPropertyChanged property, object remove_targetdata)
257         {
258             return JudgeRemoveEventHandler<T>(property, remove_targetdata, false);
259         }
260 
261         /// <summary> 对 INotifyCollectionChanged 的委托链删除指定Target的委托函数 </summary>
262         /// <typeparam name="T">具有线程通知的传入类型</typeparam>
263         /// <param name="collection">实现线程通知的类型</param>
264         /// <param name="remove_targetdata">将要增加的委托</param>
265         /// <returns>是否删除成功</returns>
266         public static bool RemoveCollectionEventHandler<T>(this INotifyCollectionChanged collection, object remove_targetdata)
267         {
268             return JudgeRemoveEventHandler<T>(collection, remove_targetdata, true);
269         }
270 
271     }
272 }
JisonsINotifyOfInvocationList

此项为 FindField

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Reflection;
  5 using System.Text;
  6 
  7 namespace Jisons
  8 {
  9     public static class JisonsFindClassInfo
 10     {
 11 
 12         /// <summary> 从当前类型一直向上查找指定名称的字段 </summary>
 13         /// <param name="type"> 查找的起始类型 </param>
 14         /// <param name="fieldName"> 字段的名称 </param>
 15         /// <param name="bindingAttr"> 搜索的标志 </param>
 16         /// <returns> 返回查询到的  FieldInfo </returns>
 17         public static FieldInfo FindField(this Type type, string fieldName, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic)
 18         {
 19             if (!string.IsNullOrWhiteSpace(fieldName))
 20             {
 21                 var field = type.GetField(fieldName, bindingAttr);
 22                 if (field != null)
 23                 {
 24                     return field;
 25                 }
 26                 else if (type.BaseType != null)
 27                 {
 28                     return type.BaseType.FindField(fieldName, bindingAttr);
 29                 }
 30             }
 31             return null;
 32         }
 33 
 34         /// <summary> 从当前类型一直向上查找指定名称的属性 </summary>
 35         /// <param name="type"> 查找的起始类型 </param>
 36         /// <param name="fieldName"> 属性的名称 </param>
 37         /// <param name="bindingAttr"> 搜索的标志 </param>
 38         /// <returns> 返回查询到的  PropertyInfo </returns>
 39         public static PropertyInfo FindProperty(this Type type, string propertyName, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic)
 40         {
 41             if (!string.IsNullOrWhiteSpace(propertyName))
 42             {
 43                 var property = type.GetProperty(propertyName, bindingAttr);
 44                 if (property != null)
 45                 {
 46                     return property;
 47                 }
 48                 else if (type.BaseType != null)
 49                 {
 50                     return type.BaseType.FindProperty(propertyName, bindingAttr);
 51                 }
 52             }
 53             return null;
 54         }
 55 
 56         /// <summary> 查询当前程序集所拥有的指定类型字段字段元数据集合 </summary>
 57         /// <typeparam name="T"> 指定查询的类型 </typeparam>
 58         /// <param name="assembly"> 查选的程序集 </param>
 59         /// <param name="bindingAttr"> 查询的指定参数 </param>
 60         /// <returns> 查询到的字段元数据集合 </returns>
 61         public static IList<FieldInfo> FindFieldInAssembly<T>(this Assembly assembly, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic) where T : class
 62         {
 63             IList<FieldInfo> datas = new List<FieldInfo>();
 64 
 65             var classTypes = assembly.GetTypes();
 66             foreach (var item in classTypes)
 67             {
 68                 var fields = item.GetFields(bindingAttr);
 69                 if (fields.Count() > 0)
 70                 {
 71                     var fieldType = typeof(T);
 72                     //此处在大量数据时曾试过并行获取 Parallel.ForEach
 73                     //不幸的发现会更慢
 74                     foreach (var field in fields)
 75                     {
 76                         if (field.FieldType.Equals(fieldType))
 77                         {
 78                             datas.Add(field);
 79                         }
 80                     }
 81                 }
 82             }
 83 
 84             return datas;
 85         }
 86 
 87         /// <summary> 查询当前程序集所拥有的指定类型静态字段集合 </summary>
 88         /// <typeparam name="T"> 指定查询的类型 </typeparam>
 89         /// <param name="assembly"> 查选的程序集 </param>
 90         /// <param name="bindingAttr"> 查询的指定参数 </param>
 91         /// <returns>查询到指定静态类型的值集合</returns>
 92         public static IList<T> FindStaticFieldValueInAssembly<T>(this Assembly assembly, BindingFlags bindingAttr = BindingFlags.Static | BindingFlags.Public) where T : class
 93         {
 94             IList<T> datas = new List<T>();
 95 
 96             var fieldList = assembly.FindFieldInAssembly<T>(bindingAttr);
 97             if (fieldList.Count > 0)
 98             {
 99                 //此处在大量数据时曾试过并行获取 Parallel.ForEach
100                 //不幸的发现会更慢
101                 fieldList.ForEach(i =>
102                 {
103                     var data = i.GetValue(null) as T;
104                     if (data != null)
105                     {
106                         datas.Add(data);
107                     }
108                 });
109             }
110 
111             return datas;
112         }
113 
114         /// <summary> 查询当前程序集所拥有的指定类型接口的接口元数据集合 </summary>
115         /// <typeparam name="T"> 指定查询类型的接口 </typeparam>
116         /// <param name="assembly"> 查选的程序集 </param>
117         /// <param name="bindingAttr"> 查询的指定参数 </param>
118         /// <returns> 查询到的字段元数据集合 </returns>
119         public static IDictionary<Type, Type> FindInterfacesInAssembly<T>(this Assembly assembly, BindingFlags bindingAttr = BindingFlags.Instance | BindingFlags.NonPublic) where T : class
120         {
121             IDictionary<Type, Type> datas = new Dictionary<Type, Type>();
122 
123             var classTypes = assembly.GetTypes();
124             var returnType = typeof(T).Name;
125             foreach (var item in classTypes)
126             {
127                 var interfaces = item.GetInterfaces();
128                 if (interfaces.Count() > 0)
129                 {
130                     var interfaceOfT = interfaces.FirstOrDefault(i => i.Name.Equals(returnType));
131                     if (interfaceOfT != null)
132                     {
133                         datas.Add(item, interfaceOfT);
134                     }
135                 }
136             }
137             return datas;
138         }
139 
140         /*  test
141        
142          * /// <summary> 获取继承接口而实现添加的快捷键子项集合 </summary>
143         /// <param name="currentAssembly"></param>
144         /// <returns></returns>
145         public static List<RoutedCommandsHotKeys> GetInterfaceHotKeys(this Assembly currentAssembly)
146         {
147             var referencedAssemblies = currentAssembly.GetReferencedAssemblies().ToList();
148             referencedAssemblies.Add(currentAssembly.GetName());
149 
150             List<RoutedCommandsHotKeys> datas = new List<RoutedCommandsHotKeys>();
151             Parallel.ForEach(referencedAssemblies, referencedAssembly =>
152             {
153                 var assembly = Assembly.Load(referencedAssembly);
154                 var data = assembly.FindInterfacesInAssembly<IHotKeys>();
155                 if (data != null)
156                 {
157                     foreach (var interfaceDictionary in data)
158                     {
159                         var methods = interfaceDictionary.Value.GetMethods();
160                         var returnType = typeof(List<RoutedCommandsHotKeys>);
161                         var methodOfIHotKeys = methods.FirstOrDefault(i => i.ReturnType.Equals(returnType));
162                         if (methodOfIHotKeys != null)
163                         {
164                             var property = interfaceDictionary.Key.FindProperty("CurrenttRoutedCommandsTarget", BindingFlags.Instance | BindingFlags.Public);
165                             if (property != null)
166                             {
167                                 try
168                                 {
169                                     //此时线程并不在UI线程,切要优化效率因此并不做移交到UI线程处理
170                                     //因此,实现接口的函数不能是需要在UI线程创建的class,例如UIElement
171                                     object obj = interfaceDictionary.Key.GetConstructor(Type.EmptyTypes).Invoke(null);
172                                     var value = methodOfIHotKeys.Invoke(obj, null) as List<RoutedCommandsHotKeys>;
173                                     if (value != null)
174                                     {
175                                         datas.AddRange(value);
176                                     }
177                                 }
178                                 catch (Exception ex)
179                                 {
180                                     Console.WriteLine(ex.Message);
181                                 }
182                             }
183                         }
184                     }
185                 }
186             });
187 
188             return datas;
189         }
190 
191          * */
192 
193     }
194 }
FindField
原文地址:https://www.cnblogs.com/jiailiuyan/p/3498942.html