1

我正在尝试将我的 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 后 [Giving 2 compiler errors]

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

3 回答 3

1

一种可能的解决方案

像这样使用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方法相同的逻辑。

首先,您需要实际调用您的方法:然后在这个新创建的IEnumerable<Task<bool>>tList.Select(func => func()上应用扩展方法:tList.Select(func => func()).TakeWhileAsync(result => result)

然后你等待这个并计算结果:(await tList.Select(func => func()).TakeWhileAsync(result => result)).Count()

为什么你的尝试没有奏效

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

请记住,异步函数总是返回一个任务(或 void)。选择块内的 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)

这个(来自西蒙的回答)确实可以编译,甚至可以在某些情况下工作。(其中 SynchronizationContext 不绑定到单个线程,但这是另一个很长的话题。)然而,主要的收获是阻塞异步代码是危险的,并且可能导致死锁。这是一篇非常详细的文章

于 2018-11-06T23:06:48.587 回答
0

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

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

但这仍然是一段奇怪的代码。

于 2013-05-03T01:13:06.507 回答
-1

This is simply because you cannot convert Task<bool> to bool.. as the compiler has instructed.

Change this:

.TakeWhile(result => result)

..to this:

.TakeWhile(result => result.Result)
于 2013-05-03T00:58:28.330 回答