How can I produce a Task to Unwrap

2019-06-16 11:38发布

问题:

Can someone please explain the difference between these two statements:

Task<Task> bTask = backup.BackupCurrentDatabaseAsync()
    .ContinueWith(_ => CompressArchiveAsync());
//unwrap the tasks to produce one entire task
Task t = bTask.Unwrap();

vs

Task<Task> bTask = backup.BackupCurrentDatabaseAsync()
    .ContinueWith(_ => 
{
    CompressArchiveAsync();
});
//unwrap the tasks to produce one entire task
Task t = bTask.Unwrap();

The methodsExtractArchiveAsync(), BackupCurrentDatabaseAsync(), RestoreDatabaseAsync() all return a Task.

Here, the first Continuation returns a Task<Task>. I can then Unwrap() this task to put Continuations on the resultant (inner) task.

The second version doesn't compile. The only different here is the braces around the CompressArchiveAsync().

I am trying to access the resultant (internal) Task to check the Task.Status. If I use the second method, the Task.Status is reporting the result of the BackupCurrentDatabaseAsync() task.

回答1:

.ContinueWith(_ => CompressArchiveAsync());

is equivalent to:

.ContinueWith(_ => 
{
    return CompressArchiveAsync();
});

Notice the return.

Your second code snippet doesn't compile because ContinueWith doesn't return a Task<Task>, but simply a Task, and there's nothing to unwrap.

The following is bound to a Func<Task, Task> (a function that takes a Task and returns a Task)

_ => 
{
    return CompressArchiveAsync();
}

But the following is actually bound to an Action<Task> (a function that takes a Task but doesn't return anything):

_ => 
{
    CompressArchiveAsync();
}

And the reference to the Task created by CompressArchiveAsync is never returned. Without a reference to it, you can't check the Task's status.

Note that:

  • ContinueWith<TResult>(Func<Task, TResult>) returns a Task<TResult>
  • ContinueWith(Action<Task>) returns a Task.

Therefore your ContinueWith(Func<Task, Task>) returns a Task<Task> that you can unwrap, but your ContinueWith(Action<Task>) simply returns a Task.



回答2:

The difference is in the Lambda Expression syntax.

There are 2 types of Lambdas: Expression Lambdas and Statement Lambdas. Expression Lambdas have no braces and return the result of the expression while Statement Lambdas have braces containing zero or more statements (one of them can be a return statement).

So this Expression Lambda:

_ => CompressArchiveAsync()

Is equivalent to this Statement Lambda:

_ => { return CompressArchiveAsync(); }

So, the difference is that in the first continuation you are returning a task but in the second you are not, it's just a void anonymous delegate. That's why the first continuation is a Task<Task> while the second is just a Task.



回答3:

Long comment to show 4.5 code.

If you could move to .Net 4.5 than code you are trying to write can be rewritten in more compact way with async/await which is essentially implements all that code internally:

 async Task CompleteBackup()
 {
    await backup.BackupCurrentDatabaseAsync()
    await CompressArchiveAsync());
    await .....
 }


回答4:

In the first example, you are calling ContinueWith with a Func. Therefore, it will return a Task<T>. The second try will call the ContinueWith overload with an Action, because... well it's an action, it does not return anything. So it will return a simple Task without T.