我正在寻找创建一个接受任何产生IEnumerable<T>
的任务的函数。为了说明,请考虑以下函数签名。
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);
答案 0 :(得分: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 });
}
然后你可以
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在注释中所指出的那样,如果明确指定TElement
,则可以使编译器推断出类型。
var ints = DoWork(GetInts, (int i) => i % 2 == 0).Result;
由于Task<T>
上T
不是“协变”,因此您的代码无效。您可能意识到类不能协变。