乔什·史密斯(Josh Smith)对RelayCommand的实现是否有缺陷? [英] Is Josh Smith's implementation of the RelayCommand flawed?

查看:71
本文介绍了乔什·史密斯(Josh Smith)对RelayCommand的实现是否有缺陷?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑参考 Josh Smith的文章WPF Apps with the Model-View-ViewModel设计模式,特别是RelayCommand的示例实现(图3). (此问题无需通读整篇文章.)

Consider the reference Josh Smith' article WPF Apps With The Model-View-ViewModel Design Pattern, specifically the example implementation of a RelayCommand (In Figure 3). (No need to read through the entire article for this question.)

总的来说,我认为实现非常出色,但是我对将CanExecuteChanged订阅委派给CommandManagerRequerySuggested事件有疑问. 文档> 状态:

In general, I think the implementation is excellent, but I have a question about the delegation of CanExecuteChanged subscriptions to the CommandManager's RequerySuggested event. The documentation for RequerySuggested states:

由于此事件是静态的,因此它将 仅作为弱者抓住处理者 参考.侦听的对象 这个事件应该保持强大 引用他们的事件处理程序 避免将其收集为垃圾.这 可以通过一个 专用字段并分配 处理程序作为之前或之后的值 与此事件相关.

Since this event is static, it will only hold onto the handler as a weak reference. Objects that listen for this event should keep a strong reference to their event handler to avoid it being garbage collected. This can be accomplished by having a private field and assigning the handler as the value before or after attaching to this event.

但是RelayCommand的示例实现并未对预订的处理程序进行任何维护:

Yet the sample implementation of RelayCommand does not maintain any such to the subscribed handler:

public event EventHandler CanExecuteChanged
{
    add { CommandManager.RequerySuggested += value; }
    remove { CommandManager.RequerySuggested -= value; }
}

  1. 这是否会将弱引用泄漏给RelayCommand的客户端,是否要求RelayCommand的用户理解CanExecuteChanged的实现并自己维护实时引用?
  2. 如果是这样,例如将RelayCommand的实现修改为类似于以下内容,以减轻CanExecuteChanged订户的潜在过早GC有意义吗?

  1. Does this leak the weak reference up to the RelayCommand's client, requiring that the user of the RelayCommand understand the implementation of CanExecuteChanged and maintain a live reference themselves?
  2. If so, does it make sense to, e.g., modify the implementation of RelayCommand to be something like the following to mitigate the potential premature GC of the CanExecuteChanged subscriber:

// This event never actually fires.  It's purely lifetime mgm't.
private event EventHandler canExecChangedRef;
public event EventHandler CanExecuteChanged
{
    add 
    { 
        CommandManager.RequerySuggested += value;
        this.canExecChangedRef += value;
    }
    remove 
    {
        this.canExecChangedRef -= value;
        CommandManager.RequerySuggested -= value; 
    }
}

推荐答案

我也认为此实现存在缺陷,因为它肯定会将弱引用泄漏给事件处理程序.这实际上是非常糟糕的事情.
我正在使用MVVM Light工具箱以及在其中实现的RelayCommand,它的实现方式与本文中相同.
以下代码将永远不会调用OnCanExecuteEditChanged:

I too believe this implementation is flawed, because it definitely leaks the weak reference to the event handler. This is something actually very bad.
I am using the MVVM Light toolkit and the RelayCommand implemented therein and it is implemented just as in the article.
The following code will never invoke OnCanExecuteEditChanged:

private static void OnCommandEditChanged(DependencyObject d, 
                                         DependencyPropertyChangedEventArgs e)
{
    var @this = d as MyViewBase;
    if (@this == null)
    {
        return;
    }

    var oldCommand = e.OldValue as ICommand;
    if (oldCommand != null)
    {
        oldCommand.CanExecuteChanged -= @this.OnCanExecuteEditChanged;
    }
    var newCommand = e.NewValue as ICommand;
    if (newCommand != null)
    {
        newCommand.CanExecuteChanged += @this.OnCanExecuteEditChanged;
    }
}

但是,如果我这样更改它,它将起作用:

However, if I change it like this, it will work:

private static EventHandler _eventHandler;

private static void OnCommandEditChanged(DependencyObject d,
                                         DependencyPropertyChangedEventArgs e)
{
    var @this = d as MyViewBase;
    if (@this == null)
    {
        return;
    }
    if (_eventHandler == null)
        _eventHandler = new EventHandler(@this.OnCanExecuteEditChanged);

    var oldCommand = e.OldValue as ICommand;
    if (oldCommand != null)
    {
        oldCommand.CanExecuteChanged -= _eventHandler;
    }
    var newCommand = e.NewValue as ICommand;
    if (newCommand != null)
    {
        newCommand.CanExecuteChanged += _eventHandler;
    }
}

唯一的区别?就像CommandManager.RequerySuggested文档中指出的那样,我将事件处理程序保存在一个字段中.

The only difference? Just as indicated in the documentation of CommandManager.RequerySuggested I am saving the event handler in a field.

这篇关于乔什·史密斯(Josh Smith)对RelayCommand的实现是否有缺陷?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆