假设您有一个字符串列表(或任何其他类型的字符串,仅使用字符串作为示例),例如
IEnumerable<string> fullList = ...;
和异步谓词,例如
static Task<bool> IncludeString(string s) { ... }
通过该谓词过滤列表的最简单方法是什么,具有以下约束:
我确实找到了一个解决方案,但它涉及创建一个临时列表,其中包含每个条目的谓词结果,然后使用它进行过滤。它感觉不够优雅。这是:
var includedIndices = await Task.WhenAll(fullList.Select(IncludeString));
var filteredList = fullList.Where((_, i) => includedIndices[i]);
通过简单的框架调用感觉就像是可能的,但我找不到。
答案 0 :(得分:1)
您可以创建自己需要的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>>
答案 1 :(得分:1)
它并不是特别优雅,但您可以在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
调用,返回的枚举也将不在枚举时重新枚举可枚举的源 - 它不会是惰性的,因为Task.WhenAll
没有&#39 ; t返回一个LINQy懒惰的可枚举。