Async Await In Linq .TakeWhile
本文关键字:TakeWhile Linq In Await Async | 更新日期: 2023-09-27 18:11:50
我正在尝试将我的foreach函数转换为linq函数
这是我的正常代码[工作正常]
var tList = new List<Func<Task<bool>>> { Method1, Method2 };
tList.Shuffle();
int succeed = 0;
foreach (var task in tList)
{
var result = await task();
if(!result)
break;
succeed += 1;
}
MessageBox.Show(succeed == 1 ? "Loading complete." : "Something went wrong!");
这里是转换为Linq后的[给出2个编译错误]
var tList = new List<Func<Task<bool>>> { Method1, Method2 };
tList.Shuffle();
int succeed = tList.Select(async task => await task()).TakeWhile(result => result).Count();
MessageBox.Show(succeed == 1 ? "Loading complete." : "Something went wrong!");
错误
- 无法将lambda表达式转换为委托类型'System '。Func,bool>'因为有些块中的返回类型不能隐式地转换为
委托返回类型- 不能隐式转换类型"System.Threading.Tasks"。任务'到'bool'
我想知道为什么编译器会给出这些消息,所以任何帮助都将是感激的。
注意:我也尝试了.TakeWhile(async result => await result)
与该错误
- 异步方法的返回类型必须是void、Task或Task T
Method1和Method2如果有人想要它们:
public async Task<bool> Method1()
{
await Task.Delay(1000);
Console.WriteLine("Method1");
return false;
}
public async Task<bool> Method2()
{
await Task.Delay(1000);
Console.WriteLine("Method2");
return true;
}
一个可能的解决方案
使用TakeWhileAsync扩展方法,如下所示:
public static async Task<IEnumerable<T>> TakeWhileAsync<T>(this IEnumerable<Task<T>> tasks, Func<T, bool> predicate)
{
var results = new List<T>();
foreach (var task in tasks)
{
var result = await task;
if (!predicate(result))
break;
results.Add(result);
}
return results;
}
必须有一个更优化的解决方案,但在我看来这很容易阅读。您遍历任务,等待每个任务完成,然后执行与常规TakeWhile方法相同的逻辑。
首先你需要调用你的方法:tList.Select(func => func()
,然后在这个新创建的IEnumerabletList.Select(func => func()).TakeWhileAsync(result => result)
然后等待这个并计算结果:(await tList.Select(func => func()).TakeWhileAsync(result => result)).Count()
为什么你的尝试没有成功
.Select(async task => await task()).TakeWhile(result => result)
记住 async函数总是返回一个Task(或void)。select块中的lambda返回一个IEnumerableresult => result
)也返回一个任务
.TakeWhile(async result => await result)
同样的问题,你的lambda返回一个任务(因为它是一个async "方法"),它永远不会作为LINQ谓词工作。
.TakeWhile(result => result.Result)
这个(来自Simon的回答)确实可以编译,甚至可以在某些情况下工作。(其中SynchronizationContext不绑定到单个线程,但这是另一个很长的话题。)然而,主要的收获是阻塞异步代码是危险的,并可能导致死锁。
第一段代码和第二段代码之间有一个根本的区别(它们做的不是同样的事情)
在第一个代码中,您正在迭代任务列表。在第二种情况下,你只是将每个任务转换为另一个任务- async lambda的返回类型将始终是某个任务。
因为你按顺序等待每个任务它们实际上是按顺序运行的。这就是你想要做的吗?
试试这样做(未测试):
var tList = new List<Func<Task<bool>>> { Method1, Method2 };
tList.Shuffle();
int succeed = Task.WaitAll(tList.ToArray())
.TakeWhile(result => result).Count();
MessageBox.Show(
succeed == 1
? "Loading complete."
: "Something went wrong!");
但是这段代码还是很奇怪
这仅仅是因为您无法将Task<bool>
转换为bool
。按照编译器的指示。
改变:
.TakeWhile(result => result)
. .:
.TakeWhile(result => result.Result)