【问题标题】:Should this code return a Task or Task<object>?这段代码应该返回一个任务还是任务<object>?
【发布时间】:2014-08-13 04:35:54
【问题描述】:

我正在阅读 The Nature of TaskCompletionSource,Stephen Toub 的帖子。

public static Task RunAsync(Action action)
{
    var tcs = new TaskCompletionSource<Object>();
    ThreadPool.QueueUserWorkItem(_ =>
    {
        try
        {
            action();
            tcs.SetResult(null);
        }
        catch(Exception exc) { tcs.SetException(exc); }
    });
    return tcs.Task;
}

由于我们不再关心T 的类型,我默认使用Object。然后,当Action执行成功时,SetResult仍用于将Task转换为RanToCompletion最终状态;但是,由于实际结果值无关紧要,因此使用null最后,RunAsync 返回 Task 而不是 Task&lt;Object&gt;。当然,实例化的task的类型仍然是Task&lt;Object&gt;,但是我们不需要这样引用它,这个方法的使用者也不需要关心那些实现细节。

我不是特别明白为什么该方法应该返回Task而不是Task&lt;object&gt;(这就是我强调粗体的原因)。我知道该方法设置为返回Task,但tcsTaskCompletionSource&lt;Object&gt;,而不是TaskCompletionSource(我认为这是错误的)。

【问题讨论】:

    标签: c# .net task-parallel-library task taskcompletionsource


    【解决方案1】:

    没有非通用的TaskCompletionSource,考虑到你想要的只是没有结果的任务,结果并不重要。 在这种情况下,调用者不知道也不关心任务实际上是一个Task&lt;object&gt;,调用者只是awaits,如果有一个异常就会得到一个异常。调用者不知道实际结果。

    Task&lt;T&gt; 继承自 Task 这当然有助于实现这一点


    找到返回 false 的 Task&lt;bool&gt; 或返回 0 的 Task&lt;int&gt; 也很常见。

    【讨论】:

    • 谢谢。当您更新答案时,我正要说同样的话。
    【解决方案2】:

    没有用于创建Task 实例的非泛型TaskCompletionSource 类不是Task&lt;T&gt; 的实例。当您不关心(或不提供)返回值时,这为 TaskCompletionSource&lt;T&gt; 的泛型类型参数留下了两个选项:

    1. 使用任意现有类型,例如object,作为返回类型。将值设置为null 表示任务完成。
    2. 使用特定的非公共类型,并将值设置为null,表示任务完成。

    当我创建TaskCompletionSource&lt;T&gt; 实例以提供没有返回值的Task 时,我更喜欢使用专用的非公共类型以确保消费代码不会将返回的Task 误认为是实例Task&lt;T&gt; 的结果有意义的地方。

    首先,我定义了以下类(如果它嵌套在另一个类型中,它可以是 private sealed class):

    internal sealed class VoidResult
    {
    }
    

    然后,我不使用TaskCompletionSource&lt;object&gt; 作为完成源,而是使用TaskCompletionSource&lt;VoidResult&gt;。由于调用代码无法访问VoidResult 类型,用户将无法将Task 对象转换为Task&lt;VoidResult&gt; 的实例。

    【讨论】:

      【解决方案3】:

      我不是特别明白为什么该方法应该返回Task而不是Task&lt;object&gt;

      因为当您返回Task&lt;Object&gt; 时,这意味着当此方法完成时,它将产生一些有用的Object 类型的值。在这种情况下,我们不会产生任何结果,这就是斯蒂芬选择返回 Task 的原因。

      如果我们正在处理Func&lt;Object&gt;,那么返回Task&lt;Object&gt; 是合适的,因为Func 会产生一些结果,我们可以选择返回它。

      为什么是TaskCompletionSource&lt;Object&gt;,而不是TaskCompletionSource

      因为没有这样的事情。没有非通用的TaskCompletionSource

      【讨论】:

        【解决方案4】:

        如果您返回Task&lt;object&gt;,那么var result = await RunAsync(...) 将始终返回null,因为这就是您将结果设置为的。

        客户不关心这个,所以你只返回一个Task

        理想情况下,您应该在内部使用TaskCompletionSource,而不是TaskCompletionSource&lt;object&gt;,并且只需调用类似SetCompleted() 的名称而不是SetResult(null)。但是这种类型不存在。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-01-02
          • 1970-01-01
          • 2014-03-28
          • 1970-01-01
          • 2010-09-08
          • 1970-01-01
          • 2020-09-12
          • 1970-01-01
          相关资源
          最近更新 更多