2012-12-13 24 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

匿名處理程序的生存期嚴格取決於您的選擇器對象,而不是外部變量,所以它將一直存在,直到您取消訂閱或選擇器對象被垃圾收集爲止。所以以這種方式它不會導致內存泄漏。

請注意,對於相同的對象可能有多個訂閱,因此您可能需要在某個時候取消訂閱。

相關問題