繁体   English   中英

在Linq中异步等待.TakeWhile

[英]Async Await In Linq .TakeWhile

我正在尝试将我的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.Task'隐式转换为'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()然后在新创建的IEnumerable <Task <bool >>上应用扩展方法: tList.Select(func => func()).TakeWhileAsync(result => result)

然后,您等待它并计算结果:( (await tList.Select(func => func()).TakeWhileAsync(result => result)).Count()

为什么您的尝试不起作用

.Select(async task => await task()).TakeWhile(result => result)

请记住, 异步函数始终返回Task (或void)。 select块中的lambda返回IEnumerable <Task <bool >> 在此应用.TakeWhile时,您的TakeWhile谓词( result => result )也会返回Task <bool> ,因为此时序列中的每个元素都是Task <bool> 这会导致错误,因为它应该返回bool

.TakeWhile(async result => await result)

同样的问题, 您的lambda返回一个Task (因为它是异步的“方法”),它将永远无法用作LINQ谓词。

.TakeWhile(result => result.Result)

(根据Simon的回答)该代码确实可以编译,甚至可以在某些情况下使用。 (其中SynchronizationContext没有绑定到单个线程,但这是另一个漫长的话题。)然而,主要的要点是, 阻塞异步代码很危险,并且有可能导致死锁 这是一篇非常详细的文章

第一个代码和第二个代码之间有根本的区别(它们不做相同的事情)。

在第一个代码上,您正在遍历任务列表。 在第二个步骤中,您只是将每个任务转换为另一个任务-异步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)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM