Josh Smith 对 RelayCommand 的实现有缺陷吗? [英] Is Josh Smith's implementation of the RelayCommand flawed?

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

问题描述

考虑参考 Josh Smith 的文章 WPF 应用程序与模型-视图-视图模型设计模式,特别是 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 的问题> 事件.文档RequerySuggested 状态:

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 订阅者:

  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天全站免登陆