使用表达式身体属性,我们可以创建RelayCommand
,如下所示
public RelayCommand Command => _command ?? (_command = new RelayCommand(CommandExecute));
然而,这也是可能的
public RelayCommand Command => new RelayCommand(CommandExecute);
显然每次调用Property getter
时都会创建一个新的RelayCommand。虽然我已经看过周围的评论说底层管道只创建了一个命令......
有没有人对此有明确的答案?
答案 0 :(得分:4)
有没有人对此有明确的答案?
文档不保证只检索一次属性值。所以,必须假设它可以多次检索它。
当然,在实践中,这种假定的行为可能永远不会发生。如果一个属性一旦被检索,如果属性改变通知从未发生过,那么一个属性就完全没有了,当然只读属性永远不会有属性改变的通知。
所以你可以逃脱它。但就个人而言,我不会冒险。如果底层实现发生更改,或者您的假设因任何原因而出错,那么拥有相同命令的两个或更多实例将成为一个问题,至少如果该命令引发了CanExecuteChanged
事件。我的意思是,我猜如果CanExecute()
状态永远不会改变,你可以拥有任意数量的对象副本,而且它们的工作方式完全相同。但如果它可以改变,那么你最终可能会在错误的命令对象上引发事件,这是一个没有人在听的命令。
这不仅仅是学术上的。您的代码可能有一天会使用Microsoft或其他基于XAML / MVVM的API的其他实现者,选择放弃存储命令对象引用,而是依赖于能够始终从模型对象中检索它,它模型对象本身的常见做法是从属性中检索命令对象。多次读取命令属性的场景完全合理,值得担心。
更重要的是,我没有看到 选项背后的动机。即使你可以逃脱它,“每次创造一个新的”似乎显然是错误的。懒惰的初始化似乎过于复杂的代码没有任何好处。毕竟,在创建模型对象之后,接下来几乎总会发生的事情是属性绑定到UI,因此将在那时检索命令属性。延迟初始化将最多延迟基础字段的初始化毫秒(通常比该时间少得多)。
如果你放弃懒惰的init,你可以使用自动属性:
public RelayCommand Command { get; } = new RelayCommand(CommandExecute);
没有明确的字段!好多了,恕我直言。
当然,请注意,使用该语法CommandExecute()
必须是static
成员。大多数命令确实需要访问模型实例,因此上述操作不适用于那些。
lazy-init模式受欢迎的一个原因可能是它允许使用字段初始化程序语法,作为通常“不允许在字段初始化程序中使用实例成员”规则的漏洞。
就我个人而言,我仍然会选择构造函数初始化(对于只读的自动属性(对于使用当前实例的命令,您仍然不需要显式的支持字段)。在这种情况下,Lazy-init似乎是一个过早的和错误优化。