实现 ICommand 以便一次只能 运行 一种方法,其他方法静默失败?

Implementing ICommand such that only one method may run at a time, others silently fail?

我一直在尝试以这样的方式实现 ICommand,即当它正在执行的方法是 运行ning 时,使用相同类型的命令执行的所有其他方法都会失败(即有一个静态导致执行到 return).

的 IsBusy 变量

需要注意的是,传递给 Command 构造函数的方法可以是同步的,也可以是异步的。

我认为我遇到的问题是调用方法在被调用方完成之前继续,因此将 IsBusy 设置回 false 并允许其他方法 运行。

我在做这件事时遇到了麻烦,而且我知道我可能在做一些非常愚蠢的事情。

尝试的代码:

using System;
using System.Windows.Input;
using System.Threading.Tasks;

namespace DobJenkins
{
    public class ExclusiveCommand : ICommand
    {


        private Command backingCommand;
        private static bool IsBusy = false;
        private Action action;

        public ExclusiveCommand(Action a) {
            action = a;
            Action guardedAction = (Action)WrapActionWithGuard;
            backingCommand = new Command (guardedAction);
            backingCommand.CanExecuteChanged += BackingCommand_CanExecuteChanged;
        }

        void BackingCommand_CanExecuteChanged (object sender, EventArgs e)
        {
            var ev = CanExecuteChanged;
            if (ev != null) {
                ev (this, e);
            }
        }

        public void ChangeCanExecute() {
            backingCommand.ChangeCanExecute();
        }

        #region ICommand implementation

        public event EventHandler CanExecuteChanged;

        public bool CanExecute (object parameter)
        {
            return backingCommand.CanExecute (parameter);
        }

        public async void Execute (object parameter)
        {
//            if (IsBusy) {
//                return;
//            }
//
//            IsBusy = true;
//
//            await AsyncWrapper(parameter).ContinueWith(_ => IsBusy=false);

            //await AsyncWrapper(parameter);
            //IsBusy = false;
            backingCommand.Execute(parameter);
        }

        public async Task AsyncWrapper(object parameter)
        {
            backingCommand.Execute (parameter);
        }

        private void WrapActionWithGuard() {
            if (IsBusy) {
                return;
            }
            IsBusy = true;

            action.Invoke ();

            IsBusy = false;
        }

        #endregion
    }
}

命令界面:http://i.stack.imgur.com/YfXoM.png

尝试的解决方案: 如您所见,我用几种不同的方式进行了绑定。我尝试先简单地使用逻辑,然后使用我的支持命令来执行该方法。然后我认为将方法包装在异步方法中将允许我执行 .continuewith 或至少等待它,但这也不起作用。然后我尝试将其包装在逻辑中并将其提供给我的支持命令。

问题:

如果我将异步方法传递到我的命令中,异步方法会正常执行,直到它遇到 'await',此时它将控制权交给命令并将 IsBusy 设置为 false。

我想要的行为是 IsBusy 保持为真,直到完成所有异步或同步方法。我不想让出控制权以将 IsBusy 设置为 false。

我建议你引入一个IAsyncCommand接口,并要求所有异步命令都继承自该接口:

public interface IAsyncCommand : ICommand
{
  Task ExecuteAsync(object parameter);
}

这使您能够异步使用命令。请注意,ICommand.Execute 的每个异步实现都应该是:

async void ICommand.Execute(object parameter)
{
  await ExecuteAsync(parameter);
}

那么实现包装命令就非常简单了:

public async Task ExecuteAsync(object parameter)
{
  if (IsBusy)
    return;
  IsBusy = true;

  var asyncCommand = backingCommand as IAsyncCommand;
  if (asyncCommand != null)
    await asyncCommand.ExecuteAsync(parameter);
  else
    backingCommand.Execute(parameter);

  IsBusy = false;
}

请注意,应避免使用 async void,因为(正如您所发现的),消费/判断何时完成/检测异常/单元测试非常困难。

我有一个MSDN article on async commands that you may find helpful. And on a side note, the current implementation of CanExecuteChanged in your code is incorrect;如果您要委派 CanExecute,您也应该委派 CanExecuteChanged(尽管我认为更合适的实施方式是使用 IsBusy)。