附加行为中的匿名事件处理程序会导致泄漏吗



我有以下代码。

因此,当Selector.SelectionChanged事件被引发时,它基本上执行命令(基于弱引用委托的DelegateCommand)。

public static readonly DependencyProperty SelectionCommandProperty
= DependencyProperty.RegisterAttached(
"SelectionCommand",
typeof(ICommand),
typeof(CommonUtilities),
new PropertyMetadata(null, OnSelectionCommandPropertyChanged));
private static void OnSelectionCommandPropertyChanged(
DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var selector = d as Selector;
var command = e.NewValue as ICommand;
if (selector != null && command != null)
{
selector.SelectionChanged
+= (o, args) => command.Execute(selector.SelectedItem);
}
}
public static ICommand GetSelectionCommand(DependencyObject d)
{
return d.GetValue(SelectionCommandProperty) as ICommand;
}
public static void SetSelectionCommand(DependencyObject d, ICommand value)
{
d.SetValue(SelectionCommandProperty, value);
}

请注意,上下文是静态的。

这会导致泄漏吗?我猜不会,因为据我所知,匿名处理程序将一直有效,直到所有"外部"变量(即这里的selectorcommand)的范围不适用于GC。一旦它们被GCed(当从父GUI卸载View(具有selector)和ViewModel(提供command)时会发生这种情况),匿名委托也将被取消挂起。

我就在这里吗?

以下是本例中的参考:

  • 查看:
    • 选择器
  • ViewModel:
    • ICommand
  • 选择器:
    • 匿名委托
    • ICommand
  • 匿名委托:
    • 选择器
    • ICommand

这意味着视图和视图模型可以被垃圾收集,从而使SelectorICommand保持活动状态。

垃圾收集器能够处理循环引用;因此,即使Selector引用了委托,而委托引用了Selector,这些仍然可以被垃圾收集。

然而,只要该匿名委托保持活动,ICommand就会保持活动,这仅由Selector实例的生存期决定。只要Selector被垃圾收集,委托和ICommand最终也将被垃圾收集。

因此,在简单的情况下,不,您的代码不会导致泄漏。

然而,在这种情况下,您的代码确实存在泄漏处理程序,我假设您的视图模型具有这样的属性:

public ICommand OnSelectionChanged
{
get { return _onSelectionChanged; }
private set 
{ 
_onSelectionChanged = value;
RaisePropertyChanged("OnSelectionChanged");
}
}

然后在视图中绑定,如果更改此OnSelectionChanged命令的值,则附加的属性将泄漏事件处理程序,因为您永远不会取消订阅执行旧命令的委托。

因此,不只是执行一个命令,而是执行该属性以前的所有值。

我会选择更像以下的实现:

private static void OnSelectionCommandPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var selector = d as Selector;
if (selector != null)
{
var oldCommand = e.OldValue as ICommand;
var newCommand = e.NewValue as ICommand;
if(oldCommand == null && newCommand != null)
{
selector.SelectionChanged += OnSelectionChanged;
}
else
{
selector.SelectionChanged -= OnSelectionChanged;
}
}
}
private static void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
var selector = (Selector)sender;
var command = GetSelectionCommand(selector);
if(command != null)
{
command.Execute(selector.SelectedItem);
}
}

匿名处理程序的生存期严格取决于选择器对象,而不是外部变量,因此它将一直存在,直到您取消订阅或选择器对象被垃圾收集。这样就不会造成内存泄漏。

请注意,同一对象可能有多个订阅,因此您可能需要在某个时候取消订阅。

最新更新