1

我们正在考虑将计算某些结果的工作与提交这些结果的工作分开的 API 模式是:

    interface IResults { }
    class Results : IResults { }

    Task<IResults> CalculateResultsAsync(CancellationToken ct)
    {
        return Task.Run<IResults>(() => new Results(), ct);
    }

    void CommitResults(IResults iresults)
    {
        Results results = (Results)iresults;
        // Commit the results
    }

这将允许客户端拥有一个启动某些结果计算的 UI,并知道计算何时准备就绪,然后在那时决定是否提交结果。这主要是为了帮助我们处理在计算过程中,UI会允许用户取消操作的情况。我们要确保:

  • 取消 UI 仅在操作仍可取消时显示(即,一旦我们在 CommitResults 中,就没有回头路了),所以一旦CalculateResultsAsync任务完成,我们就取消取消 UI,只要用户没有取消,继续并调用 commit 方法。
  • 我们不希望出现用户点击取消并且无论如何都会提交结果的情况(即竞争条件)。
  • 客户端永远不会使用IResults其他东西,只会将其传递回CommitResults.

问题: 一般问题是:这是一个好方法吗?具体来说:

  1. 由于客户端从不检查,因此将其拆分为两种方法感觉不对IResults,他们只是将其交还给 Commit 方法。
  2. 有没有解决这个问题的标准方法?
4

2 回答 2

3

这是一个非常标准的模式(如果不是理想的模式),尤其是当您的 Results 对象是不可变的时。我们在 Visual Studio 代码库中使用 TPL 的代码中定期执行此操作。当您的异步/并行逻辑正在处理数据时,总是存在很多快乐,而变异的废话则与之不同。

如果您熟悉或听说过“Roslyn”项目,这是我们实际上鼓励人们使用的模式。这个想法是重构可以在后台异步处理并生成一个对象,就像您的结果一样,它代表正在应用的重构的结果。然后,在 UI 线程上,任何人都可以获取这些结果对象之一并应用它,这会更新您的所有文件以包含新文本。

我确实发现整个 IResults/Results 有点奇怪——不清楚你是否使用它来隐藏自己的实现。如果空接口和强制转换使您感到困扰,您可以考虑向 IResult 添加一个提交方法,结果对象实现该方法。由你决定。

于 2013-05-10T21:48:03.437 回答
0

我不确定你为什么需要这种模式。对我来说,如果你CancellationToken在开始提交之前检查一下,你会得到完全相同的结果,但界面更简单。

于 2013-05-10T22:29:50.200 回答