Task.ContinueWith method requires task argument?

17,060

Solution 1

In all overloads of ContinueWith(), the first parameter is a delegate that takes a Task, so you can't pass a parameterless delegate to it.

I think that using lambdas is perfectly fine and that it doesn't hurt readability. And the lambda in your code is unnecessarily verbose:

Task.Factory.StartNew(MethodNoArguments).ContinueWith(_ => MethodNoArguments())

Or, as Cory Carson pointed out in a comment, you could write an extension method:

public static Task ContinueWith(this Task task, Action action)
{
    return task.ContinueWith(_ => action());
}

Solution 2

Writing clean code when you use multiple continuations is not that easy, although you can follow a few rules to make code cleaner:

  1. Use the shorter form from svick's answer
  2. Avoid chaining the continuations. Store the result of each continuation in a separate variable and then call ContinueWith in a separate line
  3. If the continuation code is long, store it in a lambda variable and then ContinueWith the lambda.
  4. Use an extension method like this "Then" method to make your code even cleaner.

You can change your code to something like this, which is slightly cleaner:

        var call1=Task.Factory.StartNew(()=>MethodNoArguments());
        var call2 = call1.ContinueWith(_ => MethodNoArguments());
        call2.Wait();

or even

        var call1 = Task.Factory.StartNew<Task>(MethodNoArguments);
        var call2 = call1.Then(MethodNoArguments);
        call2.Wait();

Stephen Toub discusses the Then extension and other ways you can clean up your code in Processing Sequences of Asynchronous Operations with Tasks

This problem is solved for good in C# 4. In C# 5 you can use the async/await keywords to create clean code that looks like the original synchronous version, eg:

    static async Task Run()
    {
        await MethodNoArguments();
        await MethodNoArguments();
    }

    static async Task MethodNoArguments()
    {
        await Task.Run(()=>Console.WriteLine("MethodNoArguments()"));
    }

Visual Studio 11 and .NET 4.5 have a Go Live license so you could probably start using them right away.

You can use the Async CTP in C# 4 to achieve the same result. You can use the Async CTP in production as it has a go live license. The downside is that you 'll have to make some small changes to your code when you move to .NET 4.5 due to differences between the CTP and .NET 4.5 (eg. CTP has TaskEx.Run instead of Task.Run).

Share:
17,060
Jason Goemaat
Author by

Jason Goemaat

Using Angular6, C#, and SQL mostly nowadays, and I enjoy programming puzzles.

Updated on June 05, 2022

Comments

  • Jason Goemaat
    Jason Goemaat almost 2 years

    I have a class with two methods, Load() and Process(). I want to be able to run these individually as background tasks, or in sequence. I like the ContinueWith() syntax, but I'm not able to get it to work. I have to take a Task parameter on the method I continue with and cannot have a Task parameter on the initial method.

    I would like to do it without lambda expressions, but am I stuck either using them, forcing a task parameter on one of the methods, or creating a third method LoadAndProcess()?

    void Run()
    {
        // doesn't work, but I'd like to do it
        //Task.Factory.StartNew(MethodNoArguments).ContinueWith(MethodNoArguments);
    
        Console.WriteLine("ContinueWith");
        Task.Factory.StartNew(MethodNoArguments).ContinueWith(MethodWithTaskArgument).Wait();
    
        Console.WriteLine("Lambda");
        Task.Factory.StartNew(() => { MethodNoArguments(); MethodNoArguments(); }).Wait();
    
        Console.WriteLine("ContinueWith Lambda");
        Task.Factory.StartNew(MethodNoArguments).ContinueWith(x => { 
                MethodNoArguments(); 
            }).Wait();
    }
    
    void MethodNoArguments()
    {
        Console.WriteLine("MethodNoArguments()");
    }
    
    void MethodWithTaskArgument(Task t = null)
    {
        Console.WriteLine("MethodWithTaskArgument()");
    }