C#如何编写一个通用方法进行await异步等待?

4

好的,标题可能不太好,但我想不出更好的名字...我的问题可能甚至与异步/等待无关,但我的问题在异步处理期间出现,所以我将以这种方式提出问题:

我有几个方法,它们创建任务列表,然后执行'await Task.WhenAll(任务列表)"。在这些方法中,正在等待的特定类型的任务是不同的。例如,有些方法正在等待Task<String>列表,而其他方法则正在等待Task<foo>列表。

我发现我需要在每个方法中对Task.WhenAll()进行一些非平凡的try/catch处理,而且该代码总是相同的。我想将该代码移动到通用方法中,然后传递任务列表,并让该通用方法发出Then WhenAll,在try/finally中包裹它。

但是我遇到的问题是,调用此方法的每个方法都将传递不同类型的Task列表,当我将参数声明为仅任务时,这会导致编译器抱怨:

methodA:
    List<Task<String>> myTaskList = ...
    ExecuteTasks(myTaskList);

methodB:
    List<Task<Foo>> myTaskList = ...
    ExecuteTasks(myTaskList);

 async Task ExecuteTasks(List<Task> taskList) {
    try {
        await Task.WhenAll(taskList)
        }
    catch {
        ..common catch handling goes here. This handling isn't really sensitive to the
        ..type of the Tasks, we just need to examine it's Status and Exception properties..
        }
    }

在上面的代码中,methodA和methodB各自有其自己的任务列表需要传递给ExecuteTasks,但问题是如何定义任务列表以便于传递给ExecuteTasks而不会出现类型不匹配的编译器错误?在非泛型世界中,我可能会将ExecuteTasks的参数定义为methodA和methodB列表类型的超类,这样编译器就可以“向上转型”,但这种方法在这里似乎行不通。(我尝试把ExecuteTasks定义为接受>,但并没有解决类型不匹配的问题)

一个简短的真实示例会更有帮助... - L.B
3个回答

2
尝试将您的ExecuteTasks应用于IEnumerable<Task>

async Task ExecuteTasks(IEnumerable<Task> taskList) {

正如@Hamish Smith所指出的那样,这是协变性的问题。

List<Task<String>> myTaskList = ...

ExecuteTasks(myTaskList);

async Task ExecuteTasks(IEnumerable<Task> taskList) {
    try {
        await Task.WhenAll(taskList)
        }
    catch {
        //..common catch handling goes here. This handling isn't really sensitive to the
        //..type of the Tasks, we just need to examine it's Status and Exception properties..
        }
}

如果仍然针对 List<Task> 进行键入,那么您可能会做出一些傻事,比如这样:
List<Task<String>> myTaskList = ...

ExecuteTasks(myTaskList);

async Task ExecuteTasks(List<Task> taskList) {
        taskList.Add(new Task<int>()) // bad stuff
}

1
var intTask1 = Task.Run(() => 1);
var intTask2 = Task.Run(() => 2);
var intTasks = new List<Task<int>> { intTask1, intTask2 };

var intExecutor = new TaskExecutor<int>();
await intExecutor.ExecuteTasks(intTasks);

var stringTask1 = Task.Run(() => "foo");
var stringTask2 = Task.Run(() => "bar");
var stringTasks = new List<Task<string>> { stringTask1, stringTask2 };

var stringExecutor = new TaskExecutor<string>();
await stringExecutor.ExecuteTasks(stringTasks);

..................................

class TaskExecutor<T>
{
    public async Task ExecuteTasks(IEnumerable<Task<T>> tasks)
    {
        try
        {
            await Task.WhenAll(tasks);
        }
        catch (Exception ex)
        {
            // Handle exception
        }
    }
}

类和方法的命名不好,但是重要的是使用泛型 :) - trydis

0

虽然我应该真正指向Eric Lippert关于逆变和协变以及编译器如何看待泛型的系列文章(http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx)...
但我想知道一个泛型方法是否会在这里起作用?

  async Task ExecuteTasks<T>(List<Task<T>> taskList) 
  {
     try 
     {
        await Task.WhenAll(taskList);
     }
     catch 
     {
        //..common catch handling goes here. This handling isn't really sensitive to the
        //..type of the Tasks, we just need to examine it's Status and Exception properties..
     }
  }

你需要同时拥有一个接受 List<Task>(非泛型)的重载方法,因为 Task 可以是泛型或非泛型。 - Servy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接