【问题标题】:How do I define a function that accepts any Task producing an IEnumerable<T>?如何定义一个函数来接受任何生成 IEnumerable<T> 的任务?
【发布时间】:2014-12-17 08:56:25
【问题描述】:

我正在寻找一个函数来接受任何产生IEnumerable&lt;T&gt; 的任务。为了说明,请考虑以下函数签名。

void DoWork<TElement>(Task<IEnumerable<TElement>> task)
{ }

现在,我想这样调用这个方法:

Task<int[]> task = Task.FromResult(new[] { 1, 2, 3 });
DoWork(task);

显然,这不起作用,因为这两个 Task 类型不一样,并且任务不存在协方差。但是,我想知道是否有一些聪明的技巧可以让它发挥作用,受以下示例的启发。

async Task<IEnumerable<int>> GetTask()
{
    return await Task.FromResult(new int[] { 1, 2, 3 });
}

在这里,await 正在使用内联任务的结果有效地创建一个新任务,因此产生了类型转换的错觉。

举一个更详细的例子,我想允许用户拨打DoWork而不会给转换带来太多负担:

// Service proxy method
Task<int[]> GetInts()
{
    // simplified for brevity
    return Task.FromResult(new[] { 1, 2, 3 });
}

// Service proxy method
Task<long[]> GetLongs()
{
    // simplified for brevity
    return Task.FromResult(new[] { 100L, 200L, 300L });
}

async Task<IEnumerable<T>> DoWork<T>(Func<Task<IEnumerable<T>>> getData,
                                     Func<T, bool> predicate)
{
    return (await getData()).Where(predicate);
}

// GOAL:
DoWork(GetInts, i => i % 2 == 0);
DoWork(GetLongs, l => l % 40 == 0);

【问题讨论】:

  • 我不认为这是一个重复的问题。我知道Task&lt;&gt; 不是协变的;我在问是否有一个聪明的语法或声明可以完成协变本来可以做的事情。
  • 比如dynamic?
  • 我怀疑dynamic 确实会解决所有类型匹配问题,但我希望先找到一个静态类型的解决方案。

标签: c# .net task-parallel-library async-await


【解决方案1】:

您可以再引入一个 Type 参数并执行以下操作:

async Task<IEnumerable<TElement>> DoWork<T, TElement>(Func<Task<T>> getData,
                              Func<TElement, bool> predicate) where T : IEnumerable<TElement>
{
    return (await getData()).Where(predicate);
}

Task<int[]> GetInts()
{
    return Task.Run(() => new[] { 1, 2, 3 });
}

Task<long[]> GetLongs()
{
    return Task.Run(() => new[] { 100L, 200L, 300L });
}

那么你可以

static void Main()
{
    var ints = DoWork<int[], int>(GetInts, i => i % 2 == 0).Result;
    var longs = DoWork<long[], long>(GetLongs, i => i % 2 == 0).Result;
}

或者如 OP 在 cmets 中所述,如果您明确指定 TElement,您可以让编译器推断类型。

var ints = DoWork(GetInts, (int i) => i % 2 == 0).Result;

您的代码不起作用,因为Task&lt;T&gt; 不是T 上的“协变”。您可能知道类不能是协变的。

【讨论】:

  • 这是一个很有前途的技术。如果在 lambda 中将 i 声明为 int(或 long),也可以省略调用中的显式泛型参数。例如:DoWork(GetInts, (int i) =&gt; i % 2 == 0).
  • @SteveGuidi 酷,我试图推动编译器的负载来推断类型但失败了。忘了这个。谢谢..
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-07-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多