如何定义一个接受任何生成IEnumerable <T>的任务的函数?

Ste*_*idi 10 .net c# task-parallel-library async-await

我正在寻找创建一个接受任何产生一个任务的函数IEnumerable<T>.为了说明,请考虑以下函数签名.

void DoWork<TElement>(Task<IEnumerable<TElement>> task)
{ }
Run Code Online (Sandbox Code Playgroud)

现在,我想将此方法称为如下:

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

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

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

这里,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);
Run Code Online (Sandbox Code Playgroud)

Sri*_*vel 6

您可以再引入一个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 });
}
Run Code Online (Sandbox Code Playgroud)

然后你可以

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;
}
Run Code Online (Sandbox Code Playgroud)

或者如OP中的注释所述,如果TElement明确指定,可以使编译器推断出类型.

var ints = DoWork(GetInts, (int i) => i % 2 == 0).Result;
Run Code Online (Sandbox Code Playgroud)

您的代码不起作用,因为Task<T>不是"Covariant" T.您可能意识到类不能协变.