【问题标题】:Asynchronous anonymous parameterless method as a method parameter异步匿名无参数方法作为方法参数
【发布时间】:2015-03-30 02:35:36
【问题描述】:

我有一个班级用作我所有ViewModels 的父级。它包含一个用于调用其他方法的特定方法,并在错误时显示加载消息和消息框(主要):

public class BaseViewModel
{
    async public void Try(Func<Task> action, string errorMessage = null, string waitMessage = null)
    {
        try
        {
            if (waitMessage != null)
                ShowLoading(waitMessage);
            await action();
        }
        catch (Exception e)
        {
            ShowError(errorMessage, e);
        }
        finally
        {
            HideLoading();
        }
    }
}

它是异步的,所以我的ShowLoading 可以是动画之类的。

  1. 是否正确实施?

它将始终获得匿名 (lambda) 无参数 Tasks。我的主要问题是如何实际构建这些Tasks。假设我在ViewModelBase 的孩子中有一个Command,它在执行时调用以下方法:

private void OnMyCommandExecute()
{
    Try(() =>
    {
        Thread.Sleep(5000);
    }, "error", "please wait");
}

它无法编译,因为Not all code paths return a value in lambda expression of type 'System.Func&lt;System.Threading.Tasks.Task&gt;'。很明显,因为我们await 这个Func。这就引出了第二个问题:

  1. 在此示例中,我应该在 Try 调用中添加什么内容才能使其正常工作?

我尝试了一些非常丑陋的东西,我真的希望答案会有所不同,否则可读性会很差:

Try(async () =>
{
    return await Task.Factory.StartNew(() =>
    {
        SharePointService.Connect(Connection);
        IsConnected = true;
    });
}

它不会编译,但在这一点上,最好是这样。 return 上的错误:Since 'System.Func&lt;System.Threading.Tasks.Task&gt;' is anasyncmethod that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task&lt;T&gt;'?

【问题讨论】:

  • Try 几乎可以肯定不是async void。应该是async Task
  • @Servy 我盲目地相信你,我从来没有任何异步/等待的经验。我希望Try 尽可能透明地使用。
  • 您的更新仍然有 async void 并将所有内容卸载到 ThreadPool 线程(如果这是重点,那很好,但您应该注意这一点)
  • @I3arnon 这不是重点,我不知道它是什么:p。这有什么问题?我更新了我的“解决方案”,这样你就可以看到我正在尝试做什么。 Try 不一定是调用异步方法。
  • 您不应该为您的问题添加全新的问题,也不应该为您的问题添加您自己的答案。如果你想问一个全新的问题,那就问一个新的问题。

标签: c# asynchronous mvvm lambda async-await


【解决方案1】:

我应该在这个示例中的 Try 调用中添加什么内容才能正常工作?

您需要通过添加(令人惊讶的)async 来创建 lambda 表达式 async

Try(async () =>
{
    Thread.Sleep(5000);
}, "error", "please wait");

然而,虽然这将使您能够创建一个async 委托,但它实际上并没有什么异步的(它用Thread.Sleep 阻塞调用线程)。如果这只是一个例子,那么:

Try(async () =>
{
    await Task.Delay(5000);
}, "error", "please wait");

更好。如果不是,请不要使用async

是否正确实施?

不是真的。 async void 几乎总是应该避免(除非在 UI 事件处理程序中)。请改用async Task,并确保在某个时间点对返回的任务进行await,以确保操作完成而没有任何异常。

【讨论】:

    【解决方案2】:

    Try 接受一个返回Task 的方法。在您的第一个示例中,您提供的方法是 void

    在您的第二个示例中,您提供了一个返回 Task&lt;Task&gt; 的方法,但尝试在需要 Task(非泛型)的上下文中使用它。

    如果您想使用非异步 lambda,则只需让该 lambda 返回您要使用的 Task

    Try(()=>Task.Factory.StartNew(() =>
        {
            SharePointService.Connect(Connection);
            IsConnected = true;
        }));
    

    如果你想使用async lambda,那么你需要等待任务而不返回它:

    Try(async () => await Task.Factory.StartNew(() =>
        {
            SharePointService.Connect(Connection);
            IsConnected = true;
        }));
    

    请注意,在这里使用异步 lambda 并没有真正的目的。这两个 sn-ps 将执行相同的操作,但第二个增加了一些额外的代码膨胀开销以及运行时实际上不需要的整个状态机。

    【讨论】:

      【解决方案3】:

      为了让Try 尽可能透明,我最终做到了这一点。

      async public Task Try(Action action, string errorMessage = null, string waitMessage = null)
      {
          try
          {
              if (waitMessage != null)
              {
                  ShowLoading(waitMessage);
                  await Task.Factory.StartNew(() => action());
              }
              else
                  action();
          }
          catch (Exception e)
          {
              ShowError(errorMessage, e);
          }
          finally
          {
              HideLoading();
          }
      }
      

      因此,调用时不必使用Task.Factory.StartNewasync/await

      Try(() =>
      {
          Thread.Sleep(5000);
      }, "error", "please wait");
      

      【讨论】:

      • 该方法现在只能用于同步 CPU 绑定操作,它不能用于任何其他类型的异步操作。此外,当waitMessagenull 时,您正在同步而不是异步执行该方法,这几乎肯定是错误的。
      • 不,就是这样。如果你知道它的执行时间很短,你可能不希望有一个大的“请稍候”窗口在你的脸上闪烁。
      • 但它可能不是正在执行的 CPU 绑定工作。它可能是 IO,例如数据库请求、访问 Web 服务、文件 IO 等。有各种各样的事情可以做,需要很长时间,但过程中不使用 CPU。 CPU 密集型工作只是 许多 可能性中的 一种
      • 我不明白;如果我有一个要编写 100Mo 文本文件的方法(对于 IO 示例),该方法本身不会在文件写入之前结束,因此我的任务将一直等待到结束,不是吗?数据库请求也是如此(顺便说一下,我的程序会做什么)?我的意思是,我的Try 在那个范围内可以正常工作。
      • 如果它是异步的则不会。如果它是异步的,该方法将几乎立即结束,并简单地返回一个Task,它将在操作完成时完成。创建一个只会坐在那里等待该操作完成的线程只是浪费。这就是拥有一个实际异步应用程序而不是同步应用程序的意义。
      猜你喜欢
      • 2010-11-03
      • 1970-01-01
      • 2013-02-19
      • 1970-01-01
      • 2012-12-24
      • 1970-01-01
      • 2020-06-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多