2012-12-13 23 views
1

我有以下代码。附加行为中的匿名事件处理程序是否会导致泄漏?

所以基本上它会在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从View(其具有selector)和ViewModel(即提供command)从父GUI被卸载时将发生,则匿名委托也将被解除挂钩。

我在这儿吗?

回答

2

下面是本例中的引用:

  • 查看:
    • 选择
  • 视图模型:
    • 的ICommand
  • 选择: 个
    • 匿名委托
    • 的ICommand
  • 匿名委托:
    • 选择
    • 的ICommand

这意味着可以被垃圾收集视图和视图模型,留下SelectorICommand活着。

垃圾收集器能够处理循环引用;所以尽管Selector引用了委托,并且委托引用了Selector,但仍可以进行垃圾收集。

然而,只要这个匿名代表保持活动状态,ICommand就会保持活动状态,该活动仅由实例的生命期决定。只要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); 
    } 
} 
+0

奇妙的答案。是的,我确保加载'ICommand'的模式只有一次,即没有理由为什么需要为属性更改通知。我的'ViewModel'中包含该命令的私有变量是'readonly',并在创建VM实例时加载。另外,命令本身为'CanExecute'和'Execute'实现弱委托。 –

+0

在那种情况下,你应该很好去:) – Lukazoid

0

匿名处理程序的生存期严格取决于您的选择器对象,而不是外部变量,所以它将一直存在,直到您取消订阅或选择器对象被垃圾收集为止。所以以这种方式它不会导致内存泄漏。

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

相关问题