我从只返回 Task 而不是 Task<T> 的方法返回啥?

Posted

技术标签:

【中文标题】我从只返回 Task 而不是 Task<T> 的方法返回啥?【英文标题】:What do I return from a method that returns just Task and not Task<T>?我从只返回 Task 而不是 Task<T> 的方法返回什么? 【发布时间】:2016-10-05 10:29:44 【问题描述】:

我从D4()返回什么?

async static Task D4()

    Console.Write("Enter the divisor: ");
    var n = int.Parse(Console.ReadLine());
    Console.WriteLine((24 / n).ToString());

    // NONE OF THESE WORK
    // THE COMPILER COMPLAINS WITH AN ERROR THAT SAYS:
    // Since 'Program.D4()' is an async method that returns 'Task', 
    // a return keyword must not be followed by an object expression.
    // Did you intend to return 'Task<T>'?
    // return new TaskCompletionSource<object>().Task;
    // return Task.FromResult<object>(null);
    // return Task.FromResult<bool>(false);
    // return Task.FromResult(0);

我早些时候问过something similar,它可能在我当时的情况下有效,但我现在不记得当时的情况了。此外,this thread 似乎暗示了我正在尝试的同样事情。我不确定为什么它在我的情况下不起作用。

更新

为了完整起见,这里是相关代码的全部内容。

async static Task A4()  await B4(); 
async static Task B4()  await C4(); 
async static Task C4()  await D4(); 
async static Task D4()

    Console.Write("Enter the divisor: ");
    var n = int.Parse(Console.ReadLine());
    Console.WriteLine((24 / n).ToString());

    // return new TaskCompletionSource<object>().Task;
    // return Task.FromResult<object>(null);
    // return Task.FromResult<bool>(false);
    // return Task.FromResult(0);

【问题讨论】:

没什么...这应该可以... 您不会返回任何东西。将其视为void 方法。 在完全不考虑让它异步之前,首先要确定这个方法做什么。哪个是什么?在一个完全非异步的世界中,这个方法会返回任何东西吗? @WaterCoolrv2: 所以......如果这个方法在非异步世界中不会返回任何东西,为什么它试图在异步世界中返回一些东西? async Task 方法只需要在内部等待一些东西。 @David:它实际上没有等待任何东西......但如果没有,你会收到警告。 【参考方案1】:

您不会从以 Task 作为返回类型的函数返回任何内容。类似于你对 void 所做的事情。

但是你可以使用“return;”就像你可以对 void 做的那样。

编辑

如果你在函数减速中包含“异步”,你应该什么都不返回。

示例表单dotnetfiddle

【讨论】:

'Program.D4()': not all code paths return a value 不是所有返回Task的方法,只有那些有async关键字的方法。 我已更正,您需要在减速功能中包含异步【参考方案2】:

您可以将async 视为为您构造一个Task 包装器。如果不使用async,则可以返回一个实际的Task 对象。

您的第一个代码示例使用async,但随后您尝试返回Taskasync 不需要返回 Task

async static Task D4()

  Console.Write("Enter the divisor: ");
  var n = int.Parse(Console.ReadLine());
  Console.WriteLine((24 / n).ToString());

  return; // May be removed, since it is implicit.

您的第二个代码示例删除了async,然后尝试不返回任何内容;在这种情况下,由于async 没有为您创建Task,您需要自己创建一个:

static Task D4()

  Console.Write("Enter the divisor: ");
  var n = int.Parse(Console.ReadLine());
  Console.WriteLine((24 / n).ToString());

  return Task.FromResult(0);

第一个例子(async 为你创建了Task)更惯用。请注意,您还应该在 async 方法中使用 await

【讨论】:

非常感谢。我已经醒太久了。 如果您不使用异步,那么您可以返回一个实际的 Task 对象。 就是这样。我真的醒太久了。 记得今天有Task.CompletedTask【参考方案3】:

好吧,我返回 default(Task) 只是为了让它闭嘴。我不确定纯粹主义者会怎么说。它似乎奏效了,我认为它是无害的?

static Task D4()

    Console.Write("Enter the divisor: ");
    var n = int.Parse(Console.ReadLine());
    Console.WriteLine((24 / n).ToString());

    // return new TaskCompletionSource<object>().Task;
    // return Task.FromResult<object>(null);
    // return Task.FromResult<bool>(false);
    // return Task.FromResult(0);

    return default(Task);

对此的一个批评是调用者将不知道它收到了什么以及它的状态是什么。这是一个例子,我并不关心,但作为一般原则,我认为应该避免我所说明的内容。

一种更安全但成本更高的方法可能是这样说?

private static Task cachedNoOpTask = new Task(null);

...

return cachedNoOpTask;

【讨论】:

这将返回 null,这可能不是调用者所期望的。您的问题是关于 async 方法,而 D4 未标记为 async。您应该能够使用Task.FromResult 返回一个已完成的任务,因为Task&lt;T&gt; 扩展了Task。如果你让D4 异步,那么你不应该返回一个值。 @Lee 抱歉,D4 标记为 async,但我在尝试各种建议的过程中发布了更新的代码。那时我一定是删除了async 关键字。我已经更新了问题中的代码。 你更新的异步版本 D4 编译,虽然你应该得到一个警告关于让它 async 因为它没有 await 任何东西。【参考方案4】:

去掉 async 关键字,让它变成

static Task D4()

    return Task.FromResult(0); // or one of the others from your post

您无需等待Task.FromResult()。即使您将使用使用Task.Run() 作为方法的最后一部分来执行实际工作的任务。见Awaiting tasks: Return task or await if no code after await

【讨论】:

后半部分不正确。基本上,您应该将async Task 方法视为void 方法;普通的return; 语句可以,但不能返回值。 我不好,第二个选项确实不应该返回任何东西 不过,等待Task.FromResult 是没有意义的。由于这始终是一个已完成的任务,它会同步到达方法的末尾。 确切地说,从技术上讲,它可以编译和工作,但我认为实际实现会有所不同,所以我概述了两种可能性 我不明白为什么那里有一个“完全” - 完全删除第二部分的主体。你不需要等待任何东西。

以上是关于我从只返回 Task 而不是 Task<T> 的方法返回啥?的主要内容,如果未能解决你的问题,请参考以下文章

C#5 异步返回类型 - 澄清?

为啥 ExecutorService.submit(Runnable task) 返回 Future<?> 而不是 Future<Void>?

你如何设置一些函数在后台线程中运行但保持正常的返回类型而不是 Task<int>?

为啥 Task<T> 不是协变的?

防止针对 Task<t> 进行单元测试

返回Task而不是Task是完全错误的 ?