例如,如果我有一个实现PropertyChanged
事件INotifyPropertyChanged
接口的类型,并创建该类型的1万个实例并在其他地方注册事件处理程序。然后将为每个事件处理程序创建匹配数量的委托实例。我想减少记忆足迹,但我担心不可能避免这种情况。
我取消注册事件处理程序,并在对象处理时最终清理内存。但是,我不喜欢为每个事件处理程序创建的许多代理实例。
以下是代码:
public class MyCollectionPropertyObserver : IDisposable
{
#region Fields
private IObservableList _sourceCollection;
private readonly SynchronizedObservableHashSet<string> _propNameFilter =
new SynchronizedObservableHashSet<string>();
#endregion
#region Events
public event EventHandler<PropertyObservedInfoEventArgs> ChangeDetected;
#endregion
#region Constructor
public MyCollectionPropertyObserver(IObservableList collection)
{
_sourceCollection = collection;
_sourceCollection.CollectionChanged += WeakEventHandler.Wrap(CollectionChanged, eh => _sourceCollection.CollectionChanged -= eh);
Subscribe(_sourceCollection);
}
#endregion
#region Properties
public IObservableList SourceCollection
{
get { return _sourceCollection; }
}
public SynchronizedObservableHashSet<string> PropertyNameFilters
{
get { return _propNameFilter; }
}
#endregion
#region Event Handlers
private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
Subscribe(e.NewItems);
break;
case NotifyCollectionChangedAction.Remove:
Unsubscribe(e.OldItems);
break;
case NotifyCollectionChangedAction.Replace:
Unsubscribe(e.OldItems);
Subscribe(e.NewItems);
break;
case NotifyCollectionChangedAction.Reset:
Unsubscribe(_subscribedItems.ToList());
Subscribe(_sourceCollection);
break;
}
RaiseChangeDetected(new PropertyObservedInfoEventArgs(e.Action, e.NewItems, e.OldItems));
}
private void PropertyChanged(object sender, PropertyChangedEventArgs args)
{
if (!IsFilteredProperty(args.PropertyName)) return;
RaiseChangeDetected(new PropertyObservedInfoEventArgs(sender, args.PropertyName));
}
private void InstanceChanged(object sender, InstanceChangedEventArgs args)
{
if (!IsFilteredProperty(args.ChangedProperties)) return;
RaiseChangeDetected(new PropertyObservedInfoEventArgs(sender, args.ChangedProperties));
}
#endregion
#region Methods
private bool IsFilteredProperty(string propertyName)
{
//NOTE: It is important to perform Contains check before Count == 0. Count locks, and typically there are filtered properties
return PropertyNameFilters.Contains(propertyName) || PropertyNameFilters.Count == 0;
}
private bool IsFilteredProperty(IEnumerable<string> propertyNames)
{
if (propertyNames == null) return false;
//NOTE: It is important to perform Overlaps check before Count == 0. Count locks, and typically there are filtered properties
return PropertyNameFilters.Overlaps(propertyNames) || PropertyNameFilters.Count == 0;
}
private void Subscribe(IEnumerable entities)
{
if (entities == null) return;
foreach (var entity in entities)
{
Subscribe(entity);
}
}
private readonly SynchronizedObservableHashSet<object> _subscribedItems =
new SynchronizedObservableHashSet<object>();
private void Subscribe(object entity)
{
if (entity == null) return;
if (_subscribedItems.Contains(entity))
return;
_subscribedItems.Add(entity);
var propChange = entity as INotifyPropertyChanged;
if (propChange != null)
propChange.PropertyChanged += PropertyChanged;
var instChanged = entity as INotifyInstanceChanged;
if (instChanged != null)
instChanged.InstanceChanged += InstanceChanged;
}
private void Unsubscribe(IEnumerable entities)
{
if (entities == null) return;
foreach (var entity in entities)
{
Unsubscribe(entity);
}
}
private void Unsubscribe(object entity)
{
if (entity == null) return;
_subscribedItems.Remove(entity);
var propChanged = entity as INotifyPropertyChanged;
if (propChanged != null)
propChanged.PropertyChanged -= PropertyChanged;
var instChanged = entity as INotifyInstanceChanged;
if (instChanged != null)
instChanged.InstanceChanged -= InstanceChanged;
}
private void RaiseChangeDetected(PropertyObservedInfoEventArgs message)
{
var handler = Volatile.Read(ref ChangeDetected);
if (handler == null) return;
handler(this, message);
}
private void CleanUp()
{
if (_sourceCollection == null) return;
_sourceCollection.CollectionChanged -= CollectionChanged;
_sourceCollection = null;
Unsubscribe(_subscribedItems.ToList());
}
#endregion
#region IDisposable Members
public void Dispose()
{
Dispose(true);
}
private void Dispose(bool disposing)
{
if (disposing)
CleanUp();
}
#endregion
}
答案 0 :(得分:2)
如果事件订阅之间存在一些冗余,那么是的,您可以大大减少委托对象的数量。例子:
浪费,因为从方法组到委托的每次转换都会创建一个新的委托对象,即使目标对象和目标方法在所有对象中完全相同:
foreach( source in source_list )
{
source.PropertyChanged += this.ItHappened;
}
更好:
PropertyChangedEventHandler common = this.ItHappened;
foreach( source in source_list )
{
source.PropertyChanged += common;
}
浪费,因为虽然为每个接收器对象调用相同的方法,但委托存储目标和方法信息:
foreach( sink in sink_list )
{
source.PropertyChanged += sink.ItHappened;
}
更好:
source.PropertyChanged += delegate(sender, args) {
foreach ( sink in sink_list ) {
sink.ItHappened(sender, args);
}
}
现在代码已添加到问题中,我可以向您展示如何实现我的建议。变化
public MyCollectionPropertyObserver(IObservableList collection)
{
_sourceCollection = collection;
_sourceCollection.CollectionChanged += WeakEventHandler.Wrap(CollectionChanged, eh => _sourceCollection.CollectionChanged -= eh);
Subscribe(_sourceCollection);
}
private void Subscribe(object entity)
{
if (entity == null) return;
if (_subscribedItems.Contains(entity))
return;
_subscribedItems.Add(entity);
var propChange = entity as INotifyPropertyChanged;
if (propChange != null)
propChange.PropertyChanged += PropertyChanged; // creates a new delegate object, wasteful!
var instChanged = entity as INotifyInstanceChanged;
if (instChanged != null)
instChanged.InstanceChanged += InstanceChanged; // same problem, wasteful!
}
到
private readonly PropertyChangedEventHandler reusablePropertyChangeDelegate;
private readonly InstanceChangedEventHandler reusableInstanceChangedDelegate;
public MyCollectionPropertyObserver(IObservableList collection)
{
reusablePropertyChangeDelegate = PropertyChanged;
reusableInstanceChangeDelegate = InstanceChanged;
_sourceCollection = collection;
_sourceCollection.CollectionChanged += WeakEventHandler.Wrap(CollectionChanged, eh => _sourceCollection.CollectionChanged -= eh);
Subscribe(_sourceCollection);
}
private void Subscribe(object entity)
{
if (entity == null) return;
if (_subscribedItems.Contains(entity))
return;
_subscribedItems.Add(entity);
var propChange = entity as INotifyPropertyChanged;
if (propChange != null)
propChange.PropertyChanged += reusablePropertyChangeDelegate;
var instChanged = entity as INotifyInstanceChanged;
if (instChanged != null)
instChanged.InstanceChanged += reusableInstanceChangeDelegate;
}
您还应该更改Unsubscribe
以使用缓存的实例。