C#如何使用通用方法等待?

时间:2012-11-13 21:11:26

标签: c# generics asynchronous

好吧,糟糕的标题,但我想不出一个更好的名字。我的问题甚至可能不是特定于async / await,但我的问题是在异步处理期间出现,所以我要打扮那样:

我有几种方法可以创建任务列表,然后执行'await Task.WhenAll(任务列表)'。这些方法中正在等待的特定类型的任务有所不同。例如,某些方法正在等待列表Task<String>的{​​{1}},而其他人正在等待Task<foo>的列表。

我发现我需要在每个方法中围绕Task.WhenAll()进行一些非平凡的try / catch处理,并且代码总是相同的。我想将该代码移动到一个通用的方法,然后传入任务列表,并将那个常见的方法问题发送到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定义为采用Task<Object>,但这并未解决类型不匹配问题)

3 个答案:

答案 0 :(得分: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 :(得分: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
        }
    }
}

答案 2 :(得分: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..
     }
  }