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

时间:2012-12-13 12:51:36

标签: .net wpf attached-properties anonymous-methods

我有以下代码。

所以基本上它会在引发DelegateCommand事件时执行基于弱引用委托的命令(Selector.SelectionChanged

    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。一旦他们进行了GC,当View(具有selector)和ViewModel(即提供command)从父GUI卸载时,匿名代表也会发生不被束缚。

我在这儿吗?

2 个答案:

答案 0 :(得分:2)

以下是此示例中的参考资料:

  • 查看:
    • 选择
  • 视图模型:
    • 的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);
    }
}

答案 1 :(得分:0)

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

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