【问题标题】:C# How to have a generic method do awaiting?C# 如何让泛型方法等待?
【发布时间】:2012-11-13 21:11:26
【问题描述】:

好的,标题不好,但我想不出更好的名字。我的问题可能甚至不是特定于异步/等待的,但我的问题是在异步处理期间出现的,所以我要提出就是这样:

我有几个方法可以创建任务列表,然后执行“await Task.WhenAll(任务列表)”。在这些方法中等待的具体任务类型各不相同。例如,一些方法正在等待一个列表Task<String>,而其他人正在等待 Task<foo> 的列表。

我发现我需要在每个方法中围绕 Task.WhenAll() 进行一些重要的 try/catch 处理,并且代码始终相同。我想将该代码移到一个通用方法中,然后传入任务列表,然后将该通用方法问题处理好,然后将其包含在 try/finally 中。

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

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&lt;Object&gt; 但这并没有解决类型不匹配问题)

【问题讨论】:

  • 一个简短的真实示例将帮助我们更多...

标签: c# generics asynchronous


【解决方案1】:

尝试在IEnumerable&lt;Task&gt; 中输入您的ExecuteTasks

async Task ExecuteTasks(IEnumerable&lt;Task&gt; 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&lt;Task&gt; 输入的,那么你可以做这样的傻事:

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

ExecuteTasks(myTaskList);

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

【讨论】:

    【解决方案2】:
    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
            }
        }
    }
    

    【讨论】:

    • 类和方法的命名不好,但重要的是泛型的使用:)
    【解决方案3】:

    虽然我真的应该向您指出 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&lt;Task&gt;(非泛型)的重载,因为Task 可以是泛型或非泛型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-27
    • 1970-01-01
    • 1970-01-01
    • 2011-04-30
    相关资源
    最近更新 更多