代码之家  ›  专栏  ›  技术社区  ›  Greg D

乔希·史密斯对《关系命令》的执行有缺陷吗?

  •  41
  • Greg D  · 技术社区  · 15 年前

    考虑参考资料 Josh Smith' article WPF Apps With The Model-View-ViewModel Design Pattern ,特别是 RelayCommand (见图3)。(不需要通读这个问题的整篇文章。)

    总的来说,我认为执行情况很好,但我对 CanExecuteChanged 订阅 CommandManager RequerySuggested 事件。这个 documentation for RequerySuggested 国家:

    由于此事件是静态的,因此它将 只把处理程序当作一个弱者 参考文献。监听对象 这个活动应该保持强大 对其事件处理程序的引用 避免垃圾收集。这个 可以通过 私有字段和分配 处理程序作为之前或之后的值 附加到此事件。

    但是示例实现 中继命令 不维护订阅处理程序的任何此类内容:

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }
    
    1. 这是否泄漏弱引用到 中继命令 的客户端,要求 中继命令 了解 已更改CanExecuteChanged 并保持一个实时参考?
    2. 如果是这样,修改 关系命令 如下所示,以减轻 已更改CanExecuteChanged 用户:

      // 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; 
          }
      }
      
    5 回复  |  直到 7 年前
        1
  •  9
  •   Alex Telon Jeff Yates    7 年前

    我也相信这个实现是有缺陷的 因为它肯定会泄漏对事件处理程序的弱引用。这实际上是非常糟糕的事情。
    我正在使用MVVM Light工具包和 RelayCommand 在本文中实现,并像在本文中一样实现。
    以下代码将永远不会调用 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;
        }
    }
    

    但是,如果我这样改变它,它将工作:

    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 我正在字段中保存事件处理程序。

        2
  •  44
  •   David Schmitt    13 年前

    我在乔希的书里找到了答案 comment 论他的“ Understanding Routed Commands “文章:

    […]您必须在canExecuteChanged中使用weakEvent模式 事件。这是因为视觉元素将勾住该事件,并且 直到应用程序 关闭后,内存泄漏的可能性非常大。[…]

    理由似乎是 CanExecuteChanged 由于wpf,实现者只能对注册的处理程序弱持有 Visuals 把自己解开是愚蠢的。这最容易通过授权给 CommandManager ,谁已经这样做了。大概是出于同样的原因。

        3
  •  7
  •   Thomas Levesque    15 年前

    根据Reflector的说法,它的实现方式与 RoutedCommand 同学们,所以我想一定没事吧…除非WPF团队中有人犯了错误;

        4
  •  5
  •   Will    14 年前

    我相信它有缺陷。

    通过将事件重新路由到commandmanager,可以获得以下行为

    这确保了WPF的命令 基础设施要求所有的Relaycommand 对象,如果它们可以在任何时候执行 它请求内置命令。

    但是,当您希望通知绑定到单个命令的所有控件以重新评估canexecute状态时会发生什么?在他的实现中,您必须转到commandmanager,这意味着

    应用程序中的每个命令绑定都将重新评估

    这包括所有那些并不重要的bean,那些评估canexecute有副作用的(比如数据库访问或长时间运行的任务),那些等待收集的……就像用大锤敲碎一颗易碎的钉子。

    你必须认真考虑这样做的后果。

        5
  •  0
  •   Lazarus    15 年前

    我可能遗漏了这一点,但下面的内容是否构成了对Contractor中事件处理程序的有力引用?

        _canExecute = canExecute;