C# ICommand-我应该在Execute中调用CanExecute吗?

C# ICommand-我应该在Execute中调用CanExecute吗?,c#,wpf,design-patterns,command,C#,Wpf,Design Patterns,Command,将System.Windows.Input.ICOMAND指定为两个主要方法: interface ICommand { void Execute(object parameters); bool CanExecute(object parameters); ... } 我希望在调用Execute(…)之前,在命令支持的框架中调用CanExecute(…) 但是,在我的命令实现内部,是否有理由在我的Execute(…)实现中添加CanExecute(…)调用 e、 g: 这与我的测

将System.Windows.Input.ICOMAND指定为两个主要方法:

interface ICommand {
  void Execute(object parameters);
  bool CanExecute(object parameters);
  ...
}
我希望在调用Execute(…)之前,在命令支持的框架中调用CanExecute(…)

但是,在我的命令实现内部,是否有理由在我的Execute(…)实现中添加CanExecute(…)调用

e、 g:

这与我的测试相关,因为我可能会模拟一些接口来支持CanExecute,并且在测试执行时必须进行相同的模拟


对此有何设计思想?

Execute()
中调用
CanExecute()
可能不会造成任何伤害。如果
CanExecute()
返回
false
,则通常会阻止
Execute()
,因为它们通常绑定到UI,而不是在您自己的代码中调用。但是,在调用
Execute()
之前,没有什么强制某人手动检查
CanExecute()
,因此嵌入该检查不是一个坏主意

一些MVVM框架在调用
Execute()
之前确实会检查它。不过,它们不会抛出异常。它们只是不调用
Execute()
,因此您可能不想自己抛出异常


您可以考虑是否抛出异常,如果<代码> CANEXECUTE()<代码>在< <代码>执行>(或)代码>中返回false。如果在调用
Execute()
之后,它会做一些期望完成的事情,那么抛出是有意义的。如果调用
Execute()。正如您所说,通常框架会在执行之前调用CanExecute(例如,使按钮不可见)但是开发人员可能出于某种原因决定调用Execute方法-如果他们在不应该的时候这样做,那么添加检查将提供一个有意义的异常。

我不会像其他人那样乐观地将对
CanExecute
的调用添加到
Execute
实现中。如果您的
CanExecute
执行需要很长时间才能完成,该怎么办?这意味着在现实生活中,您的用户将等待两倍的时间—一次是在环境调用
CanExecute
时,另一次是在您调用它时


您可以添加一些标志来检查是否已经调用了
CanExecute
,但要注意使它们始终处于命令状态,以免在状态更改时错过或执行不需要的
CanExecute
调用。

我会选择一种方式或另一种方式,但不能同时选择两种方式

如果您希望用户调用CanExecute,那么不要在Execute中调用它。您已经在界面中设置了这种期望,现在您与所有开发人员签订了一份合同,这意味着与ICommand的这种交互

但是,如果您担心开发人员没有正确地使用它(您可以这样做),那么我建议将其从接口中完全删除,并将其作为实现问题

例如:

interface Command {
    void Execute(object parameters);    
}

class CommandImpl: ICommand {
    public void Execute(object parameters){
        if(!CanExecute(parameters)) throw new ApplicationException("...");
        /** Execute implementation **/
    }
    private bool CanExecute(object parameters){
        //do your check here
    }
}
这样,您的合同(接口)就清晰简洁,您就不会对CanExecute是否会被调用两次感到困惑

但是,如果您因为无法控制界面而被界面卡住,另一种解决方案可能是存储结果并按如下方式检查:

interface Command {
    void Execute(object parameters);    
    bool CanExecute(object parameters);
}

class CommandImpl: ICommand {

    private IDictionary<object, bool> ParametersChecked {get; set;}

    public void Execute(object parameters){
        if(!CanExecute(parameters)) throw new ApplicationException("...");
        /** Execute implementation **/
    }

    public bool CanExecute(object parameters){
        if (ParametersChecked.ContainsKey(parameters))
            return ParametersChecked[parameters];
        var result = ... // your check here

        //method to check the store and add or replace if necessary
        AddResultsToParametersChecked(parameters, result); 
    }
}
public void SafeExecute(object parameter) {
    if (CanExecute(parameter)) {
        Execute(parameter);
    }
}
接口命令{
void执行(对象参数);
布尔CanExecute(对象参数);
}
类CommandImpl:ICommand{
私有IDictionary参数schecked{get;set;}
公共void执行(对象参数){
如果(!CanExecute(parameters))抛出新的ApplicationException(“…”);
/**执行实现**/
}
公共布尔CanExecute(对象参数){
if(参数schecked.ContainsKey(参数))
返回参数选中[参数];
var result=…//请在此处检查
//方法检查存储并在必要时添加或替换
AddResultsTopParametersChecked(参数、结果);
}
}

程序员是出了名的懒惰,他们会调用
Execute
,而不会先调用
CanExecute

ICommand
接口更常用于WPF绑定框架,但是它是一种非常健壮和有用的模式,可以在其他地方使用

我立即从
Execute
方法调用
CanExecute
,以验证对象的状态。它有助于减少重复逻辑,并强制使用
CanExecute
方法(为什么要尽一切努力来确定他们是否可以调用一个方法而不去强制执行它呢?)。我认为多次调用
CanExecute
没有问题,因为它应该是一个快速操作


但是,如果
CanExecute
方法返回
false
,我总是记录调用
Execute
方法的结果,以便消费者知道结果。

CanExecute是MSFT将命令与按钮等UI控件集成的简单方法(我会说是hack)。如果我们将命令与按钮关联,FWK可以调用CanExecute并启用/禁用按钮。从这个意义上说,我们不应该从Execute方法调用CanExcute

但是如果我们从OOP/可重用的角度考虑,我们可以看到ICommand也可以用于非UI目的,例如调用我的命令的编排/自动化代码。在这种情况下,在调用my Execute()之前,自动化没有必要调用CanExecute。因此,如果我们想让ICommand实现一致地工作,我们需要调用CanExecute()。是的,存在性能问题,我们必须进行调整

根据我的看法,.Net framework在此命令中违反了ISP,就像它如何违反ASP.Net成员资格提供程序一样。如果我错了,请更正。

我知道
public void SafeExecute(object parameter) {
    if (CanExecute(parameter)) {
        Execute(parameter);
    }
}