以下代码......
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表达式转换为委托类型 &#39; Func&lt; int,bool&gt;&#39;。异步lambda表达式可能返回void,Task或 任务&lt; T&gt;,其中任何一个都无法转换为&#39; Func&lt; int,bool&gt;&#39;。
在线
if (test.Any(async i => await Test.TestIt(i)))
你如何使用Async Lambdas和linq?
答案 0 :(得分:5)
您无法使用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");
}
对我来说确实有些麻烦,但它实现了你的目标。
答案 1 :(得分:5)
如果您正在使用一小部分LINQ方法,我建议您关注@YuvalItzchakov's answer,因为它仅依赖于作为基类库一部分提供的组件。
如果需要针对异步序列的丰富查询功能,则可以使用Rx.NET代替。 Rx提供了多种异步序列的LINQ方法,其中一些方法与@@
一起使用 - 返回代理,即Task
:
SelectMany
答案 2 :(得分:3)
你如何使用Async Lambdas和linq?
介意我转过身来?你如何希望工作?
每当你开始处理异步流时,就会出现很多关于语义的问题。它不仅像你使用LINQ那样单独使用Where
子句。
在这种情况下,您正在寻找某种&#34; async,其中&#34;过滤器应用于同步源序列。异步代码的整个想法是异步操作可能需要不同的时间(并且您希望在该操作正在进行时释放调用线程)。
所以,第一个要回答的问题是&#34; async where&#34; 当调用过滤器时。由于源序列是同步的(数组),因此所有输入值都可立即使用。应该&#34; async在哪里&#34;同时为所有元素启动异步过滤器,还是应该一次只处理一个?
如果这是一个真实的&#34;异步,其中&#34;而不是&#34; async any&#34;,下一个问题是结果序列的排序(即 评估结果时)。如果我们同时启动所有异步过滤器,那么它们可以以与它们开始时不同的顺序完成。如果任何异步过滤器返回true
,结果序列是否会产生第一个值,或者结果序列是否应保持原始值的相同顺序(这意味着缓冲)?
不同的场景需要对这些问题给出不同的答案。 Rx能够表达任何这些答案,但它很难学习。 Async / await更容易阅读,但表达力度较差。
由于这是Any
(不像Where
一般),您只需要回答第一个问题:过滤器可以同时运行还是一次运行?
如果一次一个,那么像Yuval这样的方法就可以了:
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");
答案 3 :(得分:0)
很好的答案,但我可以看到使用Yuval Itzchakov的方法增加方法的数量。它很丑,但你可以用......
public static class AsyncExtensions
{
public static Func<T, bool> Convert<T>(Func<T, Task<bool>> func)
{
return t => func(t).Result;
}
}
并按照这样消费:
static async Task MainAsync(string[] args)
{
int[] test = new[] { 1, 2, 3, 4, 5 };
if (test.Any(i => AsyncExtensions.Convert((Func<int, Task<bool>>)(async x => await TestIt(x)))(i)))
Console.WriteLine("Contains numbers > 3");
else
Console.WriteLine("Contains numbers < 3");
}
除了丑陋之外,当然最大的问题是它的阻塞,基本上是同步代码调用异步代码。我仍然会使用Yuval Itzchakov的方法,但我希望有一种方法可以做到这一点,而无需再次为异步实现LINQ。