在Expression-Bodied Properties中调用新的RelayCommand(ICommand)是否安全

时间:2017-09-26 05:56:22

标签: c# mvvm-light icommand

使用表达式身体属性,我们可以创建RelayCommand,如下所示

public RelayCommand Command => _command ?? (_command = new RelayCommand(CommandExecute));

然而,这也是可能的

public RelayCommand Command => new RelayCommand(CommandExecute);

显然每次调用Property getter时都会创建一个新的RelayCommand。虽然我已经看过周围的评论说底层管道只创建了一个命令......

有没有人对此有明确的答案?

1 个答案:

答案 0 :(得分:4)

  

有没有人对此有明确的答案?

文档不保证只检索一次属性值。所以,必须假设它可以多次检索它。

当然,在实践中,这种假定的行为可能永远不会发生。如果一个属性一旦被检索,如果属性改变通知从未发生过,那么一个属性就完全没有了,当然只读属性永远不会有属性改变的通知。

所以你可以逃脱它。但就个人而言,我不会冒险。如果底层实现发生更改,或者您的假设因任何原因而出错,那么拥有相同命令的两个或更多实例将成为一个问题,至少如果该命令引发了CanExecuteChanged事件。我的意思是,我猜如果CanExecute()状态永远不会改变,你可以拥有任意数量的对象副本,而且它们的工作方式完全相同。但如果它可以改变,那么你最终可能会在错误的命令对象上引发事件,这是一个没有人在听的命令。

这不仅仅是学术上的。您的代码可能有一天会使用Microsoft或其他基于XAML / MVVM的API的其他实现者,选择放弃存储命令对象引用,而是依赖于能够始终从模型对象中检索它,它模型对象本身的常见做法是从属性中检索命令对象。多次读取命令属性的场景完全合理,值得担心。

更重要的是,我没有看到 选项背后的动机。即使你可以逃脱它,“每次创造一个新的”似乎显然是错误的。懒惰的初始化似乎过于复杂的代码没有任何好处。毕竟,在创建模型对象之后,接下来几乎总会发生的事情是属性绑定到UI,因此将在那时检索命令属性。延迟初始化将最多延迟基础字段的初始化毫秒(通常比该时间少得多)。

如果你放弃懒惰的init,你可以使用自动属性:

public RelayCommand Command { get; } = new RelayCommand(CommandExecute);

没有明确的字段!好多了,恕我直言。

当然,请注意,使用该语法CommandExecute()必须是static成员。大多数命令确实需要访问模型实例,因此上述操作不适用于那些。

lazy-init模式受欢迎的一个原因可能是它允许使用字段初始化程序语法,作为通常“不允许在字段初始化程序中使用实例成员”规则的漏洞。

就我个人而言,我仍然会选择构造函数初始化(对于只读的自动属性(对于使用当前实例的命令,您仍然不需要显式的支持字段)。在这种情况下,Lazy-init似乎是一个过早的错误优化。