Zachary
Zachary

Reputation: 326

Using Task<List<Task>> for asynchronous recursive operation in C#

Say I have two classes A and B that derive from an interface (say IAorB). A contains a list of IAorB objects:

public interface IAorB { ... }

public class A 
{
    ...
    public List<IAorB> Children { get; set; }
    ...
}

public class B { ... }

If I wanted apply an operation to an IAorB object and all its children (if any) asynchronously, would the following approach be recommended? If not, is there a better way?

public Task SomeOperation(IAorB object) { ... } //May take some time

public Task<List<Task>> OperateRecursively(A root)
{
    List<Task> tasks = new List<Task>();

    foreach (IAorB child in root.Children)
    {
        if (child.GetType() == typeof(B))
        {
            tasks.Add(SomeOperation(child));
        }
        else
        {
            tasks.Add(OperateRecursively(child as A));
        }
    }

    tasks.Add(SomeOperation(root));

    return tasks;
}

Optimally, OperateRecursively would return a List of Tasks that I can use Task.WaitAll() on. But this approach returns nested List<Task>s, which probably isn't optimal.

Upvotes: 1

Views: 620

Answers (1)

Camilo Terevinto
Camilo Terevinto

Reputation: 32068

I think you just got a little confused with the syntax. This should be what you are looking for:

// fix method signature, this doesn't run asynchronous code so it needs not be a Task
public IEnumerable<Task> OperateRecursively(A root)
{
    List<Task> tasks = new List<Task>();

    foreach (IAorB child in root.Children)
    {
        if (child is B)
        {
            tasks.Add(SomeOperation(child));
        }
        else
        {
            // 1. change to AddRange since you are adding multiple elements, not one
            // 2. fix name for actual recursion
            // 3. cast child to A since otherwise it wouldn't compile
            tasks.AddRange(OperateRecursivelyA(child as A));
        }
    }

    tasks.Add(SomeOperation(root));

    return tasks;
}

Which you would then simply use as

await Task.WhenAll(OperateRecursivelyA(someRoot));

Upvotes: 3

Related Questions