Linq和Async Lambdas

Mic*_*ick 6 c# linq lambda asynchronous

以下代码......

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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

给你以下错误: -

CS4010:无法将异步lambda表达式转换为委托类型'Func <int,bool>'.异步lambda表达式可能返回void,Task或Task <T>,其中任何一个都不能转换为'Func <int,bool>'.

在线上

if (test.Any(async i => await Test.TestIt(i)))
Run Code Online (Sandbox Code Playgroud)

你如何使用Async Lambdas和linq?

Yuv*_*kov 6

你无法使用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;
    }
}
Run Code Online (Sandbox Code Playgroud)

并像这样消耗它:

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");
}
Run Code Online (Sandbox Code Playgroud)

它对我来说确实有点麻烦,但它实现了你的目标.


Ste*_*ary 6

如何使用 Async Lambda 和 linq?

介意我扭转一下吗?您希望他们如何工作?

每当您开始处理异步流时,都会遇到很多有关语义的问题。它不只是Where像使用 LINQ 那样添加一个子句。

在这种情况下,您正在寻找某种应用于同步源序列的“异步位置”过滤器。异步代码的整个想法是,异步操作可能需要不同的时间(并且您希望在该操作正在进行时释放调用线程)。

因此,关于“async where”要回答的第一个问题是何时调用过滤器。由于源序列是同步的(数组),因此所有输入值都立即可用。“async where”是否应该同时启动所有元素的异步过滤器,或者一次只处理一个元素?

如果这是一个实际的“async where”而不是“async any”,那么下一个问题将是结果序列的排序(即,何时评估结果)。如果我们同时启动所有异步过滤器,那么它们可以按照与启动时不同的顺序完成。生成的异步序列是否应该在任何异步过滤器返回后立即生成其第一个值true,或者生成的序列是否应该以相同的顺序保留原始值(这意味着缓冲)?

不同的场景需要对这些问题有不同的答案。Rx 能够表达这些答案中的任何一个,但它相当难以学习。Async/await 更容易阅读,但表达能力较差。

由于这是一个Any(不像 a 那样通用Where),因此您只需回答第一个问题:过滤器可以同时运行还是一次运行一个?

如果一次一个,那么尤瓦尔这样的方法就会起作用:

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");
Run Code Online (Sandbox Code Playgroud)

如果过滤器可以同时运行,那么就像这样:

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");
Run Code Online (Sandbox Code Playgroud)


Kir*_*kiy 5

如果您正在使用一小部分LINQ方法,我建议遵循@ YuvalItzchakov的答案,因为它仅依赖于作为基类库的一部分提供的组件.

如果需要对异步序列的丰富查询功能,则可以使用Rx.NET.Rx在异步序列上提供了大量LINQ方法,其中一些方法与 - Task返回委托一起工作,即SelectMany:

IEnumerable<int> numbers = Enumerable.Range(0, 10);

IEnumerable<int> evenNumbers = numbers
    .ToObservable()
    .SelectMany(async i => new { Value = i, IsMatch = await IsEven(i) })
    .Where(a => a.IsMatch)
    .Select(a => a.Value)
    .ToEnumerable();

async Task<bool> IsEven(int i)
{
    await Task.Delay(100);

    return i % 2 == 0;
}
Run Code Online (Sandbox Code Playgroud)