以下代码。。。
using System;
using System.Linq;
using System.Threading.Tasks;
namespace ConsoleAsync
{
class Program
{
static void Main(string[] args)
{
MainAsync(args).Wait();
Console.ReadLine();
}
static async Task MainAsync(string[] args)
{
int[] test = new[] { 1, 2, 3, 4, 5 };
if (test.Any(async i => await TestIt(i)))
Console.WriteLine("Contains numbers > 3");
else
Console.WriteLine("Contains numbers <= 3");
}
public static async Task<bool> TestIt(int i)
{
return await Task.FromResult(i > 3);
}
}
}
给您以下错误:-
CS4010:无法将异步lambda表达式转换为委托类型'函数<int,bool>'。异步lambda表达式可能返回void、Task或任务<T>,其中没有一个可转换为'Func<int,bool>'。
在线
if (test.Any(async i => await Test.TestIt(i)))
如何使用Async Lambdas和linq?
使用LINQ无法开箱即用。但是你可以写一个小的扩展方法来实现这一点:
public static class AsyncExtensions
{
public static async Task<bool> AnyAsync<T>(
this IEnumerable<T> source, Func<T, Task<bool>> func)
{
foreach (var element in source)
{
if (await func(element))
return true;
}
return false;
}
}
然后这样消费:
static async Task MainAsync(string[] args)
{
int[] test = new[] { 1, 2, 3, 4, 5 };
if (await test.AnyAsync(async i => await TestIt(i))
Console.WriteLine("Contains numbers > 3");
else
Console.WriteLine("Contains numbers <= 3");
}
这确实让我觉得有点麻烦,但它实现了你的目标。
如何使用Async Lambdas和linq?
介意我把它转过来吗?您希望它们如何工作?
每当您开始处理异步流时,都会有很多关于语义的问题。这不仅仅是像使用LINQ那样随意使用Where
子句。
在这种情况下,您正在寻找某种应用于同步源序列的"async where"过滤器。异步代码的全部思想是异步操作可能需要不同的时间(并且您希望在操作进行时释放调用线程)。
因此,关于"async where"的第一个问题是,当调用过滤器时。由于源序列是同步的(数组),因此所有输入值都可以立即使用。"async where"应该同时启动所有元素的异步筛选器,还是一次只处理一个?
如果这是一个实际的"async where"而不是"async any",那么下一个问题将是结果序列的排序(即,当评估结果时)。如果我们同时启动所有异步过滤器,那么它们可以按照与启动时不同的顺序完成。当任何异步过滤器返回true
时,结果异步序列应该立即产生其第一个值,还是结果序列应该保持原始值的相同顺序(这意味着缓冲)?
不同的场景需要对这些问题给出不同的答案。Rx能够表达这些答案中的任何一个,但学习起来相当困难。Async/await更易于阅读,但表达能力较差。
由于这是一个Any
(不像Where
那样通用),您只需要回答第一个问题:过滤器是同时运行还是一次运行一个?
如果一次一个,那么像尤瓦尔的方法就会奏效:
bool found = false;
foreach (var i in test)
{
if (await TestIt(i))
{
found = true;
break;
}
}
if (found)
Console.WriteLine("Contains numbers > 3");
else
Console.WriteLine("Contains numbers <= 3");
如果过滤器可以同时运行,那么如下所示:
var tasks = test.Select(i => TestIt(i)).ToList();
bool found = false;
while (tasks.Count != 0)
{
var completed = await Task.WhenAny(tasks);
tasks.Remove(completed);
if (await completed)
{
found = true;
break;
}
}
if (found)
Console.WriteLine("Contains numbers > 3");
else
Console.WriteLine("Contains numbers <= 3");
如果您使用的是LINQ方法的一小部分,我建议遵循@YuvalItzchakov的答案,因为它只依赖于作为基类库一部分的组件。
如果需要通过异步序列提供丰富的查询功能,则可以使用Rx.NET。Rx在异步序列上提供了一系列LINQ方法,其中一些方法与Task
返回的委托一起工作,即SelectMany
:
IEnumerable<int> numbers = Enumerable.Range(0, 10);
IEnumerable<int> evenNumbers = numbers
.ToObservable()
.SelectMany(async i => new { Value = i, IsMatch = await IsEven(i) })
.Where(a => a.IsMatch)
.Select(a => a.Value)
.ToEnumerable();
async Task<bool> IsEven(int i)
{
await Task.Delay(100);
return i % 2 == 0;
}