8

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.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
Simon
  • 9,197
  • 13
  • 72
  • 115
  • Side note: consider moving to 4.5 and async/awiat if possible... Will produce easier to read code... – Alexei Levenkov Dec 17 '14 at 16:26
  • Unfortunately still on 4! How would the above be achieved in 4.5? – Simon Dec 17 '14 at 16:28
  • `async`/`await` is mainty a C# 5.0 feature. While it only works out of the box when targeting .NET 4.5, you can use the async targeting pack to [port it to .NET 4.0](http://stackoverflow.com/questions/9110472/using-async-await-on-net-4). – CodesInChaos Dec 17 '14 at 16:41

4 Answers4

11
.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:

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

Lucas Trzesniewski
  • 50,214
  • 11
  • 107
  • 158
4

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.

i3arnon
  • 113,022
  • 33
  • 324
  • 344
2

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 .....
 }
Alexei Levenkov
  • 98,904
  • 14
  • 127
  • 179
  • Thanks Alexei, If I have `TaskContinuationOptions` associated with my `ContinueWith()`'s, would this method be more verbose? – Simon Dec 17 '14 at 16:36
  • @Simon If you need very detailed control over tasks you'll likely need to still use `ContinueWith` and similar calls directly. I have not done it so no direct advice. – Alexei Levenkov Dec 17 '14 at 16:58
  • 1
    @Simon: I recommend you use this `await`-based code. You can use it with .NET 4.0 Desktop by installing the [Microsoft.Bcl.Async package](https://www.nuget.org/packages/Microsoft.Bcl.Async/). With `await`, all the appropriate `TaskContinuationOptions` are already provided, and there is no need for that level of control. – Stephen Cleary Dec 17 '14 at 17:00
  • @Stephen Cleary - thank you, I will take the advise from the task master – Simon Dec 18 '14 at 02:13
  • Nitpicking: async/await is C#5 feature (which targets .NET 4.5+, but as Stephen Cleary mentioned can also be used on .NET 4.0 with that package) – Maverick Meerkat May 06 '18 at 13:59
1

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.

nvoigt
  • 75,013
  • 26
  • 93
  • 142