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;
}

Async Await In Linq .TakeWhile

一个可能的解决方案

使用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(),然后在这个新创建的IEnumerable>: tList.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返回一个IEnumerable>。当您应用时。TakeWhile在这一点上,您的TakeWhile谓词(result => result)也返回一个任务,因为在那个点上序列的每个元素都是一个任务!这会导致错误,因为它应该返回bool

.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)