我有以下代码。
因此,当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);
}
请注意,上下文是静态的。
这会导致泄漏吗?我猜不会,因为据我所知,匿名处理程序将一直有效,直到所有"外部"变量(即这里的selector
、command
)的范围不适用于GC。一旦它们被GCed(当从父GUI卸载View
(具有selector
)和ViewModel
(提供command
)时会发生这种情况),匿名委托也将被取消挂起。
我就在这里吗?
以下是本例中的参考:
- 查看:
- 选择器
- ViewModel:
- ICommand
- 选择器:
- 匿名委托
- ICommand
- 匿名委托:
- 选择器
- ICommand
这意味着视图和视图模型可以被垃圾收集,从而使Selector
和ICommand
保持活动状态。
垃圾收集器能够处理循环引用;因此,即使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);
}
}
匿名处理程序的生存期严格取决于选择器对象,而不是外部变量,因此它将一直存在,直到您取消订阅或选择器对象被垃圾收集。这样就不会造成内存泄漏。
请注意,同一对象可能有多个订阅,因此您可能需要在某个时候取消订阅。