Ожидание списка асинхронных задач, которые имеют полиморфные результаты - PullRequest
2 голосов
/ 11 декабря 2019

Проблема, которую мне нужно решить, состоит в том, чтобы иметь разные вызовы, которые возвращают задачи, но они могут содержать или не содержать базовый или некоторый производный класс, а затем ожидать все эти вызовы, прежде чем продолжить.

Следующее, нерабочий, код иллюстрирует проблему:

class Base {

}

class Derived : Base {

}

public void Main() {
    List<Task<Base>> taskList = new List<Task<Base>>();

    for ( var i = 1; i < 10; i++) {
        switch (i%2) {
            case 0:
                taskList.Add(GetDerivedAsync());
                break;
            default:
                taskList.Add(GetBaseAsync());
                break;
        }
    }

    Task.WhenAll(taskList);
}

Приведенный выше код не работает, потому что вы не можете добавить Task<Derived> в список Task<Base>.

Как я могу сделать кучу асинхронных вызовов, чей возвращаемый объект является полиморфным по своей природе, добавить их в список и затем ждать их всех перед продолжением кода?

Ответы [ 5 ]

3 голосов
/ 11 декабря 2019

Я думаю, вам нужно будет преобразовать результат GetDerivedAsync() из Task<Derived> в Task<Base>, чтобы получить их в том же списке.

public Task<Base> GetBaseTaskFromDerived(Task<Derived> derivedTask)
{
    return await derivedTask;
}

Затем добавьте вот так: taskList.Add(GetBaseTaskFromDerived(GetDerivedAsync()));

2 голосов
/ 11 декабря 2019

Вас волнует возвращаемое значение из GetBaseAsync() или GetDerivedAsync()?

Если нет, вы можете добавить их оба к List<Task> или List<Task<object>> вместо List<Task<Base>>.

Если вы заботитесь о возвращаемом значении, одним из вариантов будет добавить interface на Base и использовать это interface в вашей декларации List<Task<T>>, как показано ниже

interface IBaseResult {
    string PropertyYouCareAbout1 { get; }
    bool PropertyYouCareAbout2 { get; }
    // ...
    int PropertyYouCareAboutN { get; }
}

class Base : IBaseResult {

}

class Derived : Base {

}

public void Main() {
    List<Task<IBaseResult>> taskList = new List<Task<IBaseResult>>();

    for ( var i = 1; i < 10; i++) {
        switch (i%2) {
            case 0:
                taskList.Add(GetDerivedAsync());
                break;
            default:
                taskList.Add(GetBaseAsync());
                break;
        }
    }

    Task.WhenAll(taskList);

    // Do somethign with results in taskList
}
1 голос
/ 11 декабря 2019

Вы можете использовать тот факт, что Task<TResult> является производным от Task, и сохранять задачи в List<Task>:

var taskList = new List<Task>();

Тогда ожидание всех задач легко.

await Task.WhenAll(taskList);

Сложная задача - как собрать результаты этих задач после ожидания в List<Base>. Это может быть сделано путем кастинга, но это не красиво и не безопасно. В приведенном ниже примере я использовал выражение-переключателя (синтаксис C # 8), чтобы уменьшить многословность приведения:

List<Base> list = taskList.Select(task => task switch
{
    Task<Base> taskBase => taskBase.Result,
    Task<Derived> taskDerived => taskDerived.Result,
    _ => throw new NotImplementedException()
}).ToList();
0 голосов
/ 11 декабря 2019

Вы можете использовать два списка, один для базовых типов и один для производных типов:

public void Main() {
    var baseTaskList = new List<Task<Base>>();
    var derivedTaskList = new List<Task<Derived>>();

    for ( var i = 1; i < 10; i++) {
        switch (i%2) {
            case 0:
                derivedTaskList.Add(GetDerivedAsync());
                break;
            default:
                baseTaskList.Add(GetBaseAsync());
                break;
        }
    }

    Task.WhenAll(baseTaskList);
    Task.WhenAll(derivedTaskList);
}

Таким образом, вам не нужно изменять ваши типы, и вы можете легко получить результатызадачи.

0 голосов
/ 11 декабря 2019

Task<T> наследует Task, так что вы можете использовать List<Task> для хранения их обоих.

List<Task> taskList = new List<Task>();
taskList.Add(GetDerivedTask());
taskList.Add(GetBaseTask());

Затем вам придется привести задачи обратно к их соответствующим типам для доступарезультат:

foreach (var task in taskList)
{
    if (task is Task<Base> baseTask)
        ProcessBase(baseTask.Result);            
    else if (task is Task<Derived> derivedTask)
        ProcessDerived(derivedTask.Result);
}
...