如何使用异步谓词筛选列表



假设你有一个字符串列表(或任何其他类型,仅以字符串为例(,例如

IEnumerable<string> fullList = ...;

和一个异步谓词,例如

static Task<bool> IncludeString(string s) { ... }

按该谓词筛选列表的最简单方法是什么,具有以下约束:

    谓词
  1. 不应按顺序运行(假设列表很长,异步谓词很慢(
  2. 生成的筛选列表应保留顺序

我确实找到了一个解决方案,但它涉及创建一个临时列表,其中包含每个条目的谓词结果,然后使用它进行过滤。只是感觉不够优雅。在这里:

var includedIndices = await Task.WhenAll(fullList.Select(IncludeString));
var filteredList = fullList.Where((_, i) => includedIndices[i]);

感觉就像一个简单的框架调用应该可以实现的事情,但我找不到。

它不是特别优雅,但您可以在 select 中的谓词Task.ContinueWith调用中创建匿名类型,等待对该数组的WhenAll调用,并使用这些任务结果中包含的值。

public async Task<T[]> FilterAsync<T>(IEnumerable<T> sourceEnumerable, Func<T, Task<bool>> predicateAsync)
{
    return (await Task.WhenAll(
        sourceEnumerable.Select(
            v => predicateAsync(v)
            .ContinueWith(task => new { Predicate = task.Result, Value = v })))
        ).Where(a => a.Predicate).Select(a => a.Value).ToArray();
}

示例用法(用于演示的虚构功能(:

// Returns { "ab", "abcd" } after 1000ms
string[] evenLengthStrings = await FilterAsync<string>(new string[] { "a", "ab", "abc", "abcd" }, (async s => { await Task.Delay(1000); return s.Length % 2 == 0; }));

请注意,即使没有 ToArray 调用,返回的 enumerable 也不会在枚举时重新枚举源 enumerable - 它不会是惰性的,因为Task.WhenAll不会返回 LINQy 惰性枚举。

您可以创建自己所需的Linq函数的实现,即

public static async Task<IEnumerable<TIn>> FilterAsync<TIn>(this IEnumerable<TIn> source, Func<TIn, Task<bool>> action)
{
    if (source == null) throw new ArgumentNullException(nameof(source));
    if (action == null) throw new ArgumentNullException(nameof(action));
    var result = new List<TIn>();
    foreach (var item in source)
    {
        if (await action(item))
        {
            result.Add(item);
        }
    }
    return result;
}

然后你可以这样使用它

IEnumerable<string> example = new List<string> { "a", "", null, "   ", "e" };
var validStrings = await example.FilterAsync(IncludeString);
// returns { "a", "e" }

鉴于这种IncludeString的实现

public static Task<bool> IncludeString(string s) {
    return Task.FromResult(!string.IsNullOrWhiteSpace(s));
}

所以它基本上为列表中的每个项目运行一个async Func<int, Task<bool>>

最新更新