【问题标题】:Implementing ICommand such that only one method may run at a time, others silently fail?实现 ICommand 使得一次只能运行一种方法,其他方法静默失败?
【发布时间】:2015-06-02 11:33:35
【问题描述】:

我一直在尝试以这样一种方式实现 ICommand,即当它正在执行的方法正在运行时,使用相同类型的命令执行的所有其他方法都将失败(即,有一个静态 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 或至少等待它,但这也不起作用。然后我尝试将其包装在逻辑中并将其提供给我的支持命令。

问题:

如果我将异步方法传递给我的命令,则异步方法会很好地执行,直到它遇到“等待”,此时它将控制权交给命令并将 IsBusy 设置为 false。

我想要的行为是 IsBusy 在所有异步或同步方法完成之前保持为真。我不想让控制权将 IsBusy 设置为 false。

【问题讨论】:

  • 请发Command的代码。
  • Command 是 Xamarin.Forms 提供的一个类,我不知道如何获取代码,但我会在上面发布它的接口。

标签: c# command


【解决方案1】:

我建议你引入一个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,您可能会觉得有帮助。另外,当前的implementation of CanExecuteChanged in your code is incorrect;如果你委托CanExecute,你可能也应该委托CanExecuteChanged(尽管我认为更合适的实现是使用IsBusy)。

【讨论】:

  • 我会试试这个解决方案。我在“投票”方面没有足够的声誉,但如果它有效,我可以选择你的作为答案(:这对我来说很有意义,所以我希望它有效。另外感谢你的旁注,我会的调查一下。
  • @DobJenkins:我在看到您关于 Xamarin Forms 的更新之前写了附注。 WPF上存在内存泄漏;一个普通的事件实现可能在 Xamarin Forms 上没问题,我不确定。
  • @DobJenkins:啊,我忽略了你在构造函数中创建了Command。您可以为您的构造函数创建一个重载以使其自动执行:Func<Task>Action 的异步等效项,因此您可以添加另一个采用 Func<Task> 的构造函数,这应该可以工作。
  • 谢谢!我想我现在完全理解了。我会让你知道我的实现是如何进行的。
  • 我实现了它并且它的工作!而且它易于使用(只需将其放在通常使用 Command(或 ICommand)的地方即可)。有没有办法让我“审查”它?我确信我做了很多丑陋的事情,我确实想要良好的编码习惯。我认为 Stackoverflow 不允许基于意见的主题...那么您有什么建议?
猜你喜欢
  • 1970-01-01
  • 2016-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-18
  • 1970-01-01
  • 2018-10-02
  • 1970-01-01
相关资源
最近更新 更多